<commit_before>use super::{SyscallMeta, SyscallParam, Category, Flags, ParamType, ParamFormat};

pub enum Syscalls {
    GenericEnter = 0,
    GenericExit = 1,
    OpenEnter = 2,
    OpenExit = 3,
    CloseEnter = 4,
    CloseExit = 5,
    ReadEnter = 6,
    ReadExit = 7,
    WriteEnter = 8,
    WriteExit = 9,
    Brk1Enter = 10,
    Brk1Exit = 11,
    Execve8Enter = 12,
    Execve8Exit = 13,
    Clone11Enter = 14,
    Clone11Exit = 15,
    ProcExitEnter = 16,
    ProcExitExit = 17,
    SocketEnter = 18,
    SocketExit = 19,
    BindEnter = 20,
    BindExit = 21,
    ConnectEnter = 22,
    ConnectExit = 23,
    ListenEnter = 24,
    ListenExit = 25,
    AcceptEnter = 26,
    AcceptExit = 27,
    SendEnter = 28,
    SendExit = 29,
    SendToEnter = 30,
    SendToExit = 31,
    RecvEnter = 32,
    RecvExit = 33,
    RecvFromEnter = 34,
    RecvFromExit = 35,
    ShutdownEnter = 36,
    ShutdownExit = 37,
    GetSockNameEnter = 38,
    GetSockNameExit = 39,
    GetPeerNameEnter = 40,
    GetPeerNameExit = 41,
    SocketPairEnter = 42,
    SocketPairExit = 43,
    SetSockOptEnter = 44,
    SetSockOptExit = 45,
    GetSockOptEnter = 46,
    GetSockOptExit = 47,
    SendMsgEnter = 48,
    SendMsgExit = 49,
    SendMMsgEnter = 50,
    SendMMsgExit = 51,
    RecvMsgEnter = 52,
    RecvMsgExit = 53,
    RecvMMsgEnter = 54,
    RecvMMsgExit = 55,
    Accept4Enter = 56,
    Accept4Exit = 57,
    CreatEnter = 58,
    CreatExit = 59,
    PipeEnter = 60,
    PipeExit = 61,
    EventFdEnter = 62,
    EventFdExit = 63,
    FutexEnter = 64,
    FutexExit = 65,
    StatEnter = 66,
    StatExit = 67,
    LstatEnter = 68,
    LstatExit = 69,
    FstatEnter = 70,
    FstatExit = 71,
    Stat64Enter = 72,
    Stat64Exit = 73,
    Lstat64Enter = 74,
    Lstat64Exit = 75,
    Fstat64Enter = 76,
    Fstat64Exit = 77,
    EpollWaitEnter = 78,
    EpollWaitExit = 79,
    PollEnter = 80,
    PollExit = 81,
    SelectEnter = 82,
    SelectExit = 83,
    NewSelectEnter = 84,
    NewSelectExit = 85,

}

pub struct SyscallTable {
    syscall_metas: Vec<SyscallMeta>
}

impl Default for SyscallTable {

    fn default() -> SyscallTable {
        SyscallTable {
            syscall_metas: vec![
                SyscallMeta{ name: "syscall", category: Category::Other, flags: vec![Flags::None], nparams: 2, params: vec![SyscallParam { name: "id", kind: ParamType::SyscallId, fmt: ParamFormat::Dec }, SyscallParam { name: "native_id", kind: ParamType::UInt16, fmt: ParamFormat::Dec } ] },
                SyscallMeta{ name: "syscall", category: Category::Other, flags: vec![Flags::None], nparams: 1, params: vec![SyscallParam { name: "id", kind: ParamType::SyscallId, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "open", category: Category::File, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "open", category: Category::File, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 4, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec}, SyscallParam {name: "name", kind: ParamType::FsPath, fmt: ParamFormat::Na}, SyscallParam { name: "flags", kind: ParamType::Flags32, fmt: ParamFormat::Hex}, SyscallParam {name: "mode", kind: ParamType::UInt32, fmt: ParamFormat::Hex} ] },
                SyscallMeta{ name: "close", category: Category::IOOther, flags: vec![Flags::DestroysFd, Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "close", category: Category::IOOther, flags: vec![Flags::DestroysFd, Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "read", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd], nparams: 2, params: vec![SyscallParam{ name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam{ name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "read", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd], nparams: 2, params: vec![SyscallParam{ name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam{ name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "write", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd], nparams: 2, params: vec![SyscallParam{ name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam{ name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "write", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd], nparams: 2, params: vec![SyscallParam{ name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam{ name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "brk", category: Category::Memory, flags: vec![Flags::OldVesion], nparams: 1, params: vec![SyscallParam { name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "brk", category: Category::Memory, flags: vec![Flags::OldVesion], nparams: 1, params: vec![SyscallParam { name: "res", kind: ParamType::UInt64, fmt: ParamFormat::Hex }]},
                SyscallMeta{ name: "execve", category: Category::Process, flags: vec![Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "execve", category: Category::Process, flags: vec![Flags::ModifiesState, Flags::OldVesion], nparams: 8, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "exe", kind: ParamType::CharBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "args", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "tid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "pid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "ptid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "cwd", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "fdlimit", kind: ParamType::UInt64, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "clone", category: Category::Process, flags: vec![Flags::ModifiesState], nparams: 0, params: vec![]},
            ]

        }
    }
}

impl SyscallTable {
    pub fn get_syscall_meta(&self, id: usize) -> Option<&SyscallMeta> {
        self.syscall_metas.get(id)
    }
}
<commit_msg>extended syscall's table<commit_after>use super::{SyscallMeta, SyscallParam, Category, Flags, ParamType, ParamFormat};

pub enum Syscalls {
    GenericEnter = 0,
    GenericExit = 1,
    OpenEnter = 2,
    OpenExit = 3,
    CloseEnter = 4,
    CloseExit = 5,
    ReadEnter = 6,
    ReadExit = 7,
    WriteEnter = 8,
    WriteExit = 9,
    Brk1Enter = 10,
    Brk1Exit = 11,
    Execve8Enter = 12,
    Execve8Exit = 13,
    Clone11Enter = 14,
    Clone11Exit = 15,
    ProcExitEnter = 16,
    ProcExitExit = 17,
    SocketEnter = 18,
    SocketExit = 19,
    BindEnter = 20,
    BindExit = 21,
    ConnectEnter = 22,
    ConnectExit = 23,
    ListenEnter = 24,
    ListenExit = 25,
    AcceptEnter = 26,
    AcceptExit = 27,
    SendEnter = 28,
    SendExit = 29,
    SendToEnter = 30,
    SendToExit = 31,
    RecvEnter = 32,
    RecvExit = 33,
    RecvFromEnter = 34,
    RecvFromExit = 35,
    ShutdownEnter = 36,
    ShutdownExit = 37,
    GetSockNameEnter = 38,
    GetSockNameExit = 39,
    GetPeerNameEnter = 40,
    GetPeerNameExit = 41,
    SocketPairEnter = 42,
    SocketPairExit = 43,
    SetSockOptEnter = 44,
    SetSockOptExit = 45,
    GetSockOptEnter = 46,
    GetSockOptExit = 47,
    SendMsgEnter = 48,
    SendMsgExit = 49,
    SendMMsgEnter = 50,
    SendMMsgExit = 51,
    RecvMsgEnter = 52,
    RecvMsgExit = 53,
    RecvMMsgEnter = 54,
    RecvMMsgExit = 55,
    Accept4Enter = 56,
    Accept4Exit = 57,
    CreatEnter = 58,
    CreatExit = 59,
    PipeEnter = 60,
    PipeExit = 61,
    EventFdEnter = 62,
    EventFdExit = 63,
    FutexEnter = 64,
    FutexExit = 65,
    StatEnter = 66,
    StatExit = 67,
    LstatEnter = 68,
    LstatExit = 69,
    FstatEnter = 70,
    FstatExit = 71,
    Stat64Enter = 72,
    Stat64Exit = 73,
    Lstat64Enter = 74,
    Lstat64Exit = 75,
    Fstat64Enter = 76,
    Fstat64Exit = 77,
    EpollWaitEnter = 78,
    EpollWaitExit = 79,
    PollEnter = 80,
    PollExit = 81,
    SelectEnter = 82,
    SelectExit = 83,
    NewSelectEnter = 84,
    NewSelectExit = 85,

}

pub struct SyscallTable {
    syscall_metas: Vec<SyscallMeta>
}

impl Default for SyscallTable {

    fn default() -> SyscallTable {
        SyscallTable {
            syscall_metas: vec![
                SyscallMeta{ name: "syscall", category: Category::Other, flags: vec![Flags::None], nparams: 2, params: vec![SyscallParam { name: "id", kind: ParamType::SyscallId, fmt: ParamFormat::Dec }, SyscallParam { name: "native_id", kind: ParamType::UInt16, fmt: ParamFormat::Dec } ] },
                SyscallMeta{ name: "syscall", category: Category::Other, flags: vec![Flags::None], nparams: 1, params: vec![SyscallParam { name: "id", kind: ParamType::SyscallId, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "open", category: Category::File, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "open", category: Category::File, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 4, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec}, SyscallParam {name: "name", kind: ParamType::FsPath, fmt: ParamFormat::Na}, SyscallParam { name: "flags", kind: ParamType::Flags32, fmt: ParamFormat::Hex}, SyscallParam {name: "mode", kind: ParamType::UInt32, fmt: ParamFormat::Hex} ] },
                SyscallMeta{ name: "close", category: Category::IOOther, flags: vec![Flags::DestroysFd, Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "close", category: Category::IOOther, flags: vec![Flags::DestroysFd, Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "read", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam{ name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "read", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam{ name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "write", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam{ name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "write", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam{ name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "brk", category: Category::Memory, flags: vec![Flags::OldVesion], nparams: 1, params: vec![SyscallParam { name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "brk", category: Category::Memory, flags: vec![Flags::OldVesion], nparams: 1, params: vec![SyscallParam { name: "res", kind: ParamType::UInt64, fmt: ParamFormat::Hex }]},
                SyscallMeta{ name: "execve", category: Category::Process, flags: vec![Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "execve", category: Category::Process, flags: vec![Flags::ModifiesState, Flags::OldVesion], nparams: 8, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "exe", kind: ParamType::CharBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "args", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "tid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "pid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "ptid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "cwd", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "fdlimit", kind: ParamType::UInt64, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "clone", category: Category::Process, flags: vec![Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "clone", category: Category::Process, flags: vec![Flags::ModifiesState], nparams: 11, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam{ name: "exe", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam{ name: "args", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "tid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "pid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "ptid", kind: ParamType::Pid, fmt: ParamFormat::Dec }, SyscallParam { name: "cwd", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "fdlimit", kind: ParamType::UInt64, fmt: ParamFormat::Dec }, SyscallParam { name: "flags", kind: ParamType::Flags32, fmt: ParamFormat::Hex }, SyscallParam { name: "uid", kind: ParamType::UInt32, fmt: ParamFormat::Dec }, SyscallParam { name: "gid", kind: ParamType::UInt32, fmt: ParamFormat::Dec } ]},
                SyscallMeta{ name: "procexit", category: Category::Process, flags: vec![Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "procexit", category: Category::Process, flags: vec![Flags::Unused], nparams: 0, params: vec![]},
                SyscallMeta{ name: "socket", category: Category::Net, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 2, params: vec![SyscallParam { name: "domain", kind: ParamType::Flags32, fmt: ParamFormat::Dec }, SyscallParam{ name: "proto", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "socket", category: Category::Net, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "bind", category: Category::Net, flags: vec![Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "bind", category: Category::Net, flags: vec![Flags::UsesFd, Flags::ModifiesState], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "addr", kind: ParamType::SockAddr, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "connect", category: Category::Net, flags: vec![Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "connect", category: Category::Net, flags: vec![Flags::UsesFd, Flags::ModifiesState], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "tuple", kind: ParamType::SockAddr, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "listen", category: Category::Net, flags: vec![Flags::UsesFd], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "backlog", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "listen", category: Category::Net, flags: vec![Flags::UsesFd], nparams: 1, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "accept", category: Category::Net, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 0, params: vec![]},
                SyscallMeta{ name: "accept", category: Category::Net, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 3, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "tuple", kind: ParamType::SockTuple, fmt: ParamFormat::Na }, SyscallParam { name: "queuepct", kind: ParamType::UInt8, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "send", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "send", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Na }, SyscallParam { name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "sendto", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd, Flags::ModifiesState], nparams: 3, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }, SyscallParam { name: "tuple", kind: ParamType::SockTuple, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "sendto", category: Category::IOWrite, flags: vec![Flags::UsesFd, Flags::WritesToFd, Flags::ModifiesState], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Na }, SyscallParam { name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "recv", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "recv", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd], nparams: 2, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "recvfrom", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd, Flags::ModifiesState], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "size", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "recvfrom", category: Category::IORead, flags: vec![Flags::UsesFd, Flags::ReadsFromFd, Flags::ModifiesState], nparams: 3, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "data", kind: ParamType::ByteBuffer, fmt: ParamFormat::Na }, SyscallParam { name: "tuple", kind: ParamType::SockTuple, fmt: ParamFormat::Na }]},
                SyscallMeta{ name: "shutdown", category: Category::Net, flags: vec![Flags::UsesFd, Flags::ModifiesState], nparams: 2, params: vec![SyscallParam { name: "fd", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "how", kind: ParamType::Flags8, fmt: ParamFormat::Hex }]},
                SyscallMeta{ name: "shutdown", category: Category::Net, flags: vec![Flags::UsesFd, Flags::ModifiesState], nparams: 1, params: vec![SyscallParam { name: "fd", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "getsockname", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "getsockname", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "getpeername", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "getpeername", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "socketpair", category: Category::IPC, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 3, params: vec![SyscallParam { name: "domain", kind: ParamType::Flags32, fmt: ParamFormat::Dec }, SyscallParam { name: "type", kind: ParamType::UInt32, fmt: ParamFormat::Dec }, SyscallParam { name: "proto", kind: ParamType::UInt32, fmt: ParamFormat::Dec }]},
                SyscallMeta{ name: "socketpair", category: Category::IPC, flags: vec![Flags::CreatesFd, Flags::ModifiesState], nparams: 4, params: vec![SyscallParam { name: "res", kind: ParamType::ErrNo, fmt: ParamFormat::Dec }, SyscallParam { name: "fd1", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "fd2", kind: ParamType::Fd, fmt: ParamFormat::Dec }, SyscallParam { name: "peer", kind: ParamType::UInt64, fmt: ParamFormat::Hex }]},
                SyscallMeta{ name: "setsockopt", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "setsockopt", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "getsockopt", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },
                SyscallMeta{ name: "getsockopt", category: Category::Net, flags: vec![Flags::None], nparams: 0, params: vec![] },

            ]

        }
    }
}

impl SyscallTable {
    pub fn get_syscall_meta(&self, id: usize) -> Option<&SyscallMeta> {
        self.syscall_metas.get(id)
    }
}
<|endoftext|>


<commit_before>//! Tests for the cache file for the rustc version info.

use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{basic_manifest, project};
use std::env;

#[cargo_test]
fn rustc_info_cache() {
    // TODO: need to gate this on nightly as soon as -Cbitcode-in-rlib lands in
    // nightly
    if true {
        return;
    }

    let p = project()
        .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
        .build();

    let miss = "[..] rustc info cache miss[..]";
    let hit = "[..]rustc info cache hit[..]";
    let update = "[..]updated rustc info cache[..]";

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]failed to read rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("CARGO_CACHE_RUSTC_INFO", "0")
        .with_stderr_contains("[..]rustc info cache disabled[..]")
        .with_stderr_does_not_contain(update)
        .run();

    let other_rustc = {
        let p = project()
            .at("compiler")
            .file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
            .file(
                "src/main.rs",
                r#"
            use std::process::Command;
            use std::env;

            fn main() {
                let mut cmd = Command::new("rustc");
                for arg in env::args_os().skip(1) {
                    cmd.arg(arg);
                }
                std::process::exit(cmd.status().unwrap().code().unwrap());
            }
        "#,
            )
            .build();
        p.cargo("build").run();

        p.root()
            .join("target/debug/compiler")
            .with_extension(env::consts::EXE_EXTENSION)
    };

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    other_rustc.move_into_the_future();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();
}
<commit_msg>Auto merge of #8155 - alexcrichton:reenable-test, r=ehuss<commit_after>//! Tests for the cache file for the rustc version info.

use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{basic_manifest, project};
use std::env;

#[cargo_test]
fn rustc_info_cache() {
    // Needs `-Cbitcode-in-rlib` to ride to stable before this can be enabled
    // everywhere.
    if !cargo_test_support::is_nightly() {
        return;
    }

    let p = project()
        .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
        .build();

    let miss = "[..] rustc info cache miss[..]";
    let hit = "[..]rustc info cache hit[..]";
    let update = "[..]updated rustc info cache[..]";

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]failed to read rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("CARGO_CACHE_RUSTC_INFO", "0")
        .with_stderr_contains("[..]rustc info cache disabled[..]")
        .with_stderr_does_not_contain(update)
        .run();

    let other_rustc = {
        let p = project()
            .at("compiler")
            .file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
            .file(
                "src/main.rs",
                r#"
            use std::process::Command;
            use std::env;

            fn main() {
                let mut cmd = Command::new("rustc");
                for arg in env::args_os().skip(1) {
                    cmd.arg(arg);
                }
                std::process::exit(cmd.status().unwrap().code().unwrap());
            }
        "#,
            )
            .build();
        p.cargo("build").run();

        p.root()
            .join("target/debug/compiler")
            .with_extension(env::consts::EXE_EXTENSION)
    };

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    other_rustc.move_into_the_future();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();
}
<|endoftext|>


<commit_before>use ptr::{null, addr_of};
use result::{Result, Ok, Err};
use libc::{c_char, c_uint};
use name_pool::{name_pool, add};
use str::raw::from_c_str;
use io::WriterUtil;
use jsapi::{JSBool, JSClass, JSContext, JSErrorReport, JSFunctionSpec,
               JSObject, JSRuntime, JSString, JSVERSION_LATEST, jsval,
               JSPropertySpec, JSPropertyOp, JSStrictPropertyOp, JSProto_LIMIT};
use jsapi::bindgen::{JS_free, JS_AddObjectRoot, JS_DefineFunctions,
                        JS_DestroyContext, JS_EncodeString, JS_EvaluateScript,
                        JS_Finish, JS_GetContextPrivate, JS_GetPrivate,
                        JS_Init, JS_InitStandardClasses,
                        JS_NewGlobalObject, JS_NewContext,
                        JS_RemoveObjectRoot, JS_SetContextPrivate,
                        JS_SetErrorReporter, JS_SetOptions, JS_SetPrivate,
                        JS_SetVersion, JS_ValueToString, JS_DefineProperties,
                        JS_DefineProperty, JS_NewObject, JS_ComputeThis};
use libc::types::common::c99::{int8_t, int16_t, int32_t, int64_t, uint8_t,
                                  uint16_t, uint32_t, uint64_t};
use jsval::{JSVAL_TO_OBJECT, JSVAL_IS_PRIMITIVE};
use glue::bindgen::{RUST_JSVAL_TO_OBJECT, RUST_JSVAL_IS_PRIMITIVE};
use rust::jsobj;
pub use rust;

pub use name_pool;
pub use mod jsapi;
pub use mod glue;
pub use mod crust;

// These are just macros in jsapi.h
pub use JS_NewRuntime = jsapi::bindgen::JS_Init;
pub use JS_DestroyRuntime = jsapi::bindgen::JS_Finish;
/*
FIXME: Not sure where JS_Lock is
pub use JS_LockRuntime = jsapi::bindgen::JS_Lock;
pub use JS_UnlockRuntime = jsapi::bindgen::JS_Unlock;
*/

// FIXME: Add the remaining options
pub const JSOPTION_STRICT: uint32_t =    0b00000000000001u32;
pub const JSOPTION_WERROR: uint32_t =    0b00000000000010u32;
pub const JSOPTION_VAROBJFIX: uint32_t = 0b00000000000100u32;
//pub const JSOPTION_METHODJIT: uint32_t = 0b100000000000000u32;
pub const JSOPTION_METHODJIT: uint32_t = (1 << 14) as u32;

pub const default_heapsize: u32 = 8_u32 * 1024_u32 * 1024_u32;
pub const default_stacksize: uint = 8192u;
pub const ERR: JSBool = 0_i32;

pub const JSVAL_TAG_MAX_DOUBLE: u64 = 0x1FFF0;

pub const JSVAL_TYPE_DOUBLE: u64 = 0x00;
pub const JSVAL_TYPE_INT32: u64 = 0x01;
pub const JSVAL_TYPE_UNDEFINED: u64 = 0x02;
pub const JSVAL_TYPE_BOOLEAN: u64 = 0x03;
pub const JSVAL_TYPE_MAGIC: u64 = 0x04;
pub const JSVAL_TYPE_STRING: u64 = 0x05;
pub const JSVAL_TYPE_NULL: u64 = 0x06;
pub const JSVAL_TYPE_OBJECT: u64 = 0x07;
pub const JSVAL_TYPE_UNKNOWN: u64 = 0x20;

pub const JSVAL_TAG_SHIFT: int = 47;

//The following constants are totally broken on non-64bit platforms.
//See jsapi.h for the proper macro definitions.
pub const JSVAL_VOID: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNKNOWN) << JSVAL_TAG_SHIFT;
pub const JSVAL_NULL: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) << JSVAL_TAG_SHIFT;
pub const JSVAL_ZERO: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT;
pub const JSVAL_ONE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT) | 1;
pub const JSVAL_FALSE: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT;
pub const JSVAL_TRUE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT) | 1;

pub const JSPROP_ENUMERATE: c_uint = 0x01;
pub const JSPROP_READONLY: c_uint  = 0x02;
pub const JSPROP_SHARED: c_uint =    0x40;
pub const JSPROP_NATIVE_ACCESSORS: c_uint = 0x08;

pub const JSCLASS_RESERVED_SLOTS_SHIFT: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_WIDTH: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint = ((1 << JSCLASS_RESERVED_SLOTS_WIDTH) - 1);

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
pub const JSCLASS_IS_GLOBAL: c_uint = (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1));

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint = JSProto_LIMIT * 3 + 24;

pub pure fn JSCLASS_HAS_RESERVED_SLOTS(n: c_uint) -> c_uint {
    (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT
}

pub fn result(n: JSBool) -> Result<(),()> {
    if n != ERR {Ok(())} else {Err(())}
}
pub fn result_obj(o: jsobj) -> Result<jsobj, ()> {
    if o.ptr != null() {Ok(o)} else {Err(())}
}

pub type named_functions = @{
    names: ~[~str],
    funcs: ~[JSFunctionSpec]
};

#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *JSContext, vp: *jsval) -> *jsval {
    ptr::offset(vp, 2u)
}

pub unsafe fn JS_SET_RVAL(_cx: *JSContext, vp: *jsval, v: jsval) {
    let vp: *mut jsval = cast::reinterpret_cast(&vp);
    *vp = v;
}

#[inline(always)]
pub unsafe fn JS_THIS_OBJECT(cx: *JSContext, vp: *jsval) -> *JSObject unsafe {
    let r = JSVAL_TO_OBJECT(
        if JSVAL_IS_PRIMITIVE(*ptr::offset(vp, 1)) {
            JS_ComputeThis(cx, vp)
        } else {
            *ptr::offset(vp, 1)
        });
    r
}

<commit_msg>Add the shadow::Object stuff and an inline GetReservedSlot<commit_after>use ptr::{null, addr_of};
use result::{Result, Ok, Err};
use libc::{c_char, c_uint};
use name_pool::{name_pool, add};
use str::raw::from_c_str;
use io::WriterUtil;
use jsapi::{JSBool, JSClass, JSContext, JSErrorReport, JSFunctionSpec,
               JSObject, JSRuntime, JSString, JSVERSION_LATEST, jsval, jsid,
               JSPropertySpec, JSPropertyOp, JSStrictPropertyOp, JSProto_LIMIT};
use jsapi::bindgen::{JS_free, JS_AddObjectRoot, JS_DefineFunctions,
                        JS_DestroyContext, JS_EncodeString, JS_EvaluateScript,
                        JS_Finish, JS_GetContextPrivate, JS_GetPrivate,
                        JS_Init, JS_InitStandardClasses,
                        JS_NewGlobalObject, JS_NewContext,
                        JS_RemoveObjectRoot, JS_SetContextPrivate,
                        JS_SetErrorReporter, JS_SetOptions, JS_SetPrivate,
                        JS_SetVersion, JS_ValueToString, JS_DefineProperties,
                        JS_DefineProperty, JS_NewObject, JS_ComputeThis};
use libc::types::common::c99::{int8_t, int16_t, int32_t, int64_t, uint8_t,
                                  uint16_t, uint32_t, uint64_t};
use jsval::{JSVAL_TO_OBJECT, JSVAL_IS_PRIMITIVE};
use glue::bindgen::{RUST_JSVAL_TO_OBJECT, RUST_JSVAL_IS_PRIMITIVE};
use rust::jsobj;
pub use rust;

pub use name_pool;
pub use mod jsapi;
pub use mod glue;
pub use mod crust;

// These are just macros in jsapi.h
pub use JS_NewRuntime = jsapi::bindgen::JS_Init;
pub use JS_DestroyRuntime = jsapi::bindgen::JS_Finish;
/*
FIXME: Not sure where JS_Lock is
pub use JS_LockRuntime = jsapi::bindgen::JS_Lock;
pub use JS_UnlockRuntime = jsapi::bindgen::JS_Unlock;
*/

// FIXME: Add the remaining options
pub const JSOPTION_STRICT: uint32_t =    0b00000000000001u32;
pub const JSOPTION_WERROR: uint32_t =    0b00000000000010u32;
pub const JSOPTION_VAROBJFIX: uint32_t = 0b00000000000100u32;
//pub const JSOPTION_METHODJIT: uint32_t = 0b100000000000000u32;
pub const JSOPTION_METHODJIT: uint32_t = (1 << 14) as u32;

pub const default_heapsize: u32 = 8_u32 * 1024_u32 * 1024_u32;
pub const default_stacksize: uint = 8192u;
pub const ERR: JSBool = 0_i32;

pub const JSVAL_TAG_MAX_DOUBLE: u64 = 0x1FFF0;

pub const JSVAL_TYPE_DOUBLE: u64 = 0x00;
pub const JSVAL_TYPE_INT32: u64 = 0x01;
pub const JSVAL_TYPE_UNDEFINED: u64 = 0x02;
pub const JSVAL_TYPE_BOOLEAN: u64 = 0x03;
pub const JSVAL_TYPE_MAGIC: u64 = 0x04;
pub const JSVAL_TYPE_STRING: u64 = 0x05;
pub const JSVAL_TYPE_NULL: u64 = 0x06;
pub const JSVAL_TYPE_OBJECT: u64 = 0x07;
pub const JSVAL_TYPE_UNKNOWN: u64 = 0x20;

pub const JSVAL_TAG_SHIFT: int = 47;

//The following constants are totally broken on non-64bit platforms.
//See jsapi.h for the proper macro definitions.
pub const JSVAL_VOID: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNKNOWN) << JSVAL_TAG_SHIFT;
pub const JSVAL_NULL: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) << JSVAL_TAG_SHIFT;
pub const JSVAL_ZERO: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT;
pub const JSVAL_ONE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT) | 1;
pub const JSVAL_FALSE: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT;
pub const JSVAL_TRUE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT) | 1;

pub const JSPROP_ENUMERATE: c_uint = 0x01;
pub const JSPROP_READONLY: c_uint  = 0x02;
pub const JSPROP_SHARED: c_uint =    0x40;
pub const JSPROP_NATIVE_ACCESSORS: c_uint = 0x08;

pub const JSCLASS_RESERVED_SLOTS_SHIFT: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_WIDTH: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint = ((1 << JSCLASS_RESERVED_SLOTS_WIDTH) - 1);

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
pub const JSCLASS_IS_GLOBAL: c_uint = (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1));

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint = JSProto_LIMIT * 3 + 24;

pub pure fn JSCLASS_HAS_RESERVED_SLOTS(n: c_uint) -> c_uint {
    (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT
}

pub fn result(n: JSBool) -> Result<(),()> {
    if n != ERR {Ok(())} else {Err(())}
}
pub fn result_obj(o: jsobj) -> Result<jsobj, ()> {
    if o.ptr != null() {Ok(o)} else {Err(())}
}

pub type named_functions = @{
    names: ~[~str],
    funcs: ~[JSFunctionSpec]
};

#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *JSContext, vp: *jsval) -> *jsval {
    ptr::offset(vp, 2u)
}

pub unsafe fn JS_SET_RVAL(_cx: *JSContext, vp: *jsval, v: jsval) {
    let vp: *mut jsval = cast::reinterpret_cast(&vp);
    *vp = v;
}

#[inline(always)]
pub unsafe fn JS_THIS_OBJECT(cx: *JSContext, vp: *jsval) -> *JSObject unsafe {
    let r = JSVAL_TO_OBJECT(
        if JSVAL_IS_PRIMITIVE(*ptr::offset(vp, 1)) {
            JS_ComputeThis(cx, vp)
        } else {
            *ptr::offset(vp, 1)
        });
    r
}

// This is a duplication of the shadow stuff from jsfriendapi.h.  Here
// there be dragons!
mod shadow {
    struct TypeObject {
        proto: *JSObject
    }

    struct BaseShape {
        clasp: *JSClass,
        parent: *JSObject
    }

    struct Shape {
        base: *BaseShape,
        _1: jsid,
        slotInfo: u32
    }
    const FIXED_SLOTS_SHIFT: u32 = 27;

    pub struct Object {
        shape: *Shape,
        objType: *TypeObject,
        slots: *jsval,
        _1: *jsval,
        // Hack so we can point to the first slot
        fixedSlot0: jsval
    }

    impl Object {
        #[inline(always)]
        pure fn numFixedSlots() -> libc::size_t unsafe {
            ((*self.shape).slotInfo >> FIXED_SLOTS_SHIFT) as libc::size_t
        }

        #[inline(always)]
        fn fixedSlots() -> *jsval {
            ptr::to_unsafe_ptr(&self.fixedSlot0)
        }

        // Like slotRef, but just returns the value, not a reference
        #[inline(always)]
        pure fn slotVal(slot: libc::size_t) -> jsval unsafe {
            let nfixed : libc::size_t = self.numFixedSlots();
            if slot < nfixed {
                return *ptr::offset(self.fixedSlots(), slot as uint)
            }
            return *ptr::offset(self.slots, (slot - nfixed) as uint)
        }
    }
}

#[inline(always)]
pub unsafe fn GetReservedSlot(obj: *JSObject, slot: libc::size_t) -> jsval {
    let s = obj as *shadow::Object;
    return (*s).slotVal(slot)
}
<|endoftext|>


<commit_before>use time;
use std::time::duration::Duration;
use std::old_io::timer;

pub fn cycle_anim<'a, T>(period_s: f64, frames: &'a [T]) -> &'a T {
    assert!(period_s > 0.0);
    assert!(frames.len() > 0);
    let idx = (time::precise_time_s() / period_s) as usize % frames.len();

    &frames[idx]
}

pub fn single_anim<'a, T>(start_s: f64, period_s: f64, frames: &'a [T]) -> &'a T {
    assert!(period_s > 0.0);
    assert!(frames.len() > 0);
    let mut idx = ((time::precise_time_s() - start_s) / period_s) as i32;
    if idx < 0 { idx = 0; }
    if idx >= frames.len() as i32 { idx = frames.len() as i32 - 1; }

    &frames[idx as usize]
}

#[derive(Copy)]
pub struct Ticker {
    period_s: f64,
    last_t: f64,
}

impl Ticker {
    pub fn new(period_s: f64) -> Ticker {
        Ticker {
            period_s: period_s,
            last_t: time::precise_time_s()
        }
    }

    fn time_remaining(&mut self) -> Option<f64> {
        let now = time::precise_time_s();
        if now - self.last_t > self.period_s {
            if now - self.last_t > self.period_s * 2.0 {
                // Bring the clock up to speed if running very late.
                self.last_t = now;
            } else {
                self.last_t += self.period_s;
            }
            None
        } else {
            Some(self.period_s - (now - self.last_t))
        }
    }

    pub fn get(&mut self) -> bool {
        self.time_remaining().is_none()
    }

    pub fn wait_for_tick(&mut self) {
        match self.time_remaining() {
            Some(t) => {
                timer::sleep(Duration::milliseconds((t * 1000.0) as i64));
                self.last_t += self.period_s;
            }
            _ => {}
        }
    }
}

#[derive(Copy)]
pub struct TimePerFrame {
    update_weight: f64,
    start_t: f64,
    pub average: f64,
    pub last: f64,
}

impl TimePerFrame {
    pub fn new(update_weight: f64) -> TimePerFrame {
        assert!(update_weight >= 0.0 && update_weight <= 1.0);
        TimePerFrame {
            update_weight: update_weight,
            start_t: time::precise_time_s(),
            average: 0.0,
            last: 0.0,
        }
    }

    pub fn begin(&mut self) {
        self.start_t = time::precise_time_s();
    }

    pub fn end(&mut self) {
        self.last = time::precise_time_s() - self.start_t;
        self.average = self.update_weight * self.last +
            (1.0 - self.update_weight) * self.average;
    }
}
<commit_msg>Spike timing<commit_after>use time;
use std::time::duration::Duration;
use std::old_io::timer;

pub fn cycle_anim<'a, T>(period_s: f64, frames: &'a [T]) -> &'a T {
    assert!(period_s > 0.0);
    assert!(frames.len() > 0);
    let idx = (time::precise_time_s() / period_s) as usize % frames.len();

    &frames[idx]
}

/// Time-plot that spikes at given intervals for the given time.
pub fn spike(down_s: f64, up_s: f64) -> bool {
    time::precise_time_s() % (down_s + up_s) > down_s
}

pub fn single_anim<'a, T>(start_s: f64, period_s: f64, frames: &'a [T]) -> &'a T {
    assert!(period_s > 0.0);
    assert!(frames.len() > 0);
    let mut idx = ((time::precise_time_s() - start_s) / period_s) as i32;
    if idx < 0 { idx = 0; }
    if idx >= frames.len() as i32 { idx = frames.len() as i32 - 1; }

    &frames[idx as usize]
}

#[derive(Copy)]
pub struct Ticker {
    period_s: f64,
    last_t: f64,
}

impl Ticker {
    pub fn new(period_s: f64) -> Ticker {
        Ticker {
            period_s: period_s,
            last_t: time::precise_time_s()
        }
    }

    fn time_remaining(&mut self) -> Option<f64> {
        let now = time::precise_time_s();
        if now - self.last_t > self.period_s {
            if now - self.last_t > self.period_s * 2.0 {
                // Bring the clock up to speed if running very late.
                self.last_t = now;
            } else {
                self.last_t += self.period_s;
            }
            None
        } else {
            Some(self.period_s - (now - self.last_t))
        }
    }

    pub fn get(&mut self) -> bool {
        self.time_remaining().is_none()
    }

    pub fn wait_for_tick(&mut self) {
        match self.time_remaining() {
            Some(t) => {
                timer::sleep(Duration::milliseconds((t * 1000.0) as i64));
                self.last_t += self.period_s;
            }
            _ => {}
        }
    }
}

#[derive(Copy)]
pub struct TimePerFrame {
    update_weight: f64,
    start_t: f64,
    pub average: f64,
    pub last: f64,
}

impl TimePerFrame {
    pub fn new(update_weight: f64) -> TimePerFrame {
        assert!(update_weight >= 0.0 && update_weight <= 1.0);
        TimePerFrame {
            update_weight: update_weight,
            start_t: time::precise_time_s(),
            average: 0.0,
            last: 0.0,
        }
    }

    pub fn begin(&mut self) {
        self.start_t = time::precise_time_s();
    }

    pub fn end(&mut self) {
        self.last = time::precise_time_s() - self.start_t;
        self.average = self.update_weight * self.last +
            (1.0 - self.update_weight) * self.average;
    }
}
<|endoftext|>


<commit_before>// FIXME: Replace these examples with executable tests
//! Traits related to relationships between multiple tables.
//!
//! **Note: This feature is under active development, and we are seeking feedback on the APIs that
//! have been released. Please feel free to [open issues][open-issue], or join [our chat][gitter]
//! to provide feedback.**
//!
//! [open-issue]: https://github.com/diesel-rs/diesel/issues/new
//! [gitter]: https://gitter.im/diesel-rs/diesel
//!
//! Note: The derives in this guide are provided by `diesel_codegen`. Make sure you have
//! `#[macro_use] extern crate diesel_codegen;` at the root of your crate.
//!
//! Associations in Diesel are bidirectional, but primarily focus on the child-to-parent
//! relationship. You can declare an association between two records with `#[has_many]` and
//! `#[belongs_to]`.
//!
//! ```ignore
//! #[derive(Identifiable, Queryable, Associations)]
//! #[has_many(posts)]
//! pub struct User {
//!     id: i32,
//!     name: String,
//! }
//!
//! #[derive(Identifiable, Queryable, Associations)]
//! #[belongs_to(User)]
//! pub struct Post {
//!     id: i32,
//!     user_id: i32,
//!     title: String,
//! }
//! ```
//!
//! Note that in addition to the `#[has_many]` and `#[belongs_to]` annotations, we also need to
//! `#[derive(Associations)]`
//!
//! `#[has_many]` should be passed the name of the table that the children will be loaded from,
//! while `#[belongs_to]` is given the name of the struct that represents the parent. Both types
//! must implement the [`Identifiable`][identifiable] trait. The struct or table referenced in your
//! association has to be in scope, so you'll need `use schema::posts` or similar to bring the
//! table into scope, and `use some_module::User` if `User` were in a different module.
//!
//! [Identifiable]: trait.Identifiable.html
//!
//! `#[has_many]` actually has no behavior on its own. It only enables joining between the two
//! tables. If you are only writing `children.inner_join(parents)` or
//! `Child::belonging_to(&parents)`, you only need to define the `#[belongs_to]` side.
//!
//! Once the associations are defined, you can join between the two tables using the
//! [`inner_join`][inner-join] or [`left_outer_join`][left-outer-join].
//!
//! [inner-join]: /diesel/query_source/trait.Table.html#method.inner_join
//! [left-outer-join]: /diesel/query_source/trait.Table.html#method.left_outer_join
//!
//! ```ignore
//! let data: Vec<(User, Post)> = users::table.inner_join(posts::table).load(&connection);
//! ```
//!
//! Note: Due to language limitations, only two tables can be joined per query. This will change in
//! the future.
//!
//! Typically however, queries are loaded in multiple queries. For most datasets, the reduced
//! amount of duplicated information sent over the wire saves more time than the extra round trip
//! costs us. You can load the children for a single parent using the
//! [`belonging_to`][belonging-to]
//!
//! [belonging-to]: /diesel/prelude/trait.BelongingToDsl.html#tymethod.belonging_to
//!
//! ```ignore
//! let user = try!(users::find(1).first(&connection));
//! let posts = Post::belonging_to(&user).load(&connection);
//! ```
//!
//! If you're coming from other ORMs, you'll notice that this design is quite different from most.
//! There you would have an instance method on the parent, or have the children stored somewhere on
//! the posts. This design leads to many problems, including [N+1 query
//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an
//! association that isn't there.
//!
//! [load-your-entire-database-into-memory-lol]: http://stackoverflow.com/q/97197/1254484
//!
//! In Diesel, data and its associations are considered to be separate. If you want to pass around
//! a user and all of its posts, that type is `(User, Vec<Post>)`.
//!
//! Next lets look at how to load the children for more than one parent record.
//! [`belonging_to`][belonging-to] can be used to load the data, but we'll also need to group it
//! with its parents. For this we use an additional method [`grouped_by`][grouped-by]
//!
//! [grouped-by]: trait.GroupedBy.html#tymethod.grouped_by
//!
//! ```ignore
//! fn first_twenty_users_and_their_posts(conn: &PgConnection) -> QueryResult<Vec<(User, Vec<Post>)>> {
//!     let users = try!(users::limit(20).load::<User>(conn));
//!     let posts = try!(Post::belonging_to(&users).load::<Post>(conn));
//!     let grouped_posts = posts.grouped_by(&users);
//!     users.into_iter().zip(grouped_posts).collect()
//! }
//! ```
//!
//! [`grouped_by`][grouped-by] takes a `Vec<Child>` and a `Vec<Parent>` and returns a
//! `Vec<Vec<Child>>` where the index of the children matches the index of the parent they belong
//! to. Or to put it another way, it returns them in an order ready to be `zip`ed with the parents. You
//! can do this multiple times. For example, if you wanted to load the comments for all the posts
//! as well, you could do this: (explicit type annotations have been added for documentation
//! purposes)
//!
//! ```ignore
//! let posts: Vec<Post> = try!(Post::belonging_to(&users).load());
//! let comments: Vec<Comment> = try!(Comment::belonging_to(&posts).load());
//! let comments: Vec<Vec<Comment>> = comments.grouped_by(&posts);
//! let posts_and_comments: Vec<Vec<(Post, Vec<Comment>)>> = posts.into_iter().zip(comments).grouped_by(&users);
//! let result: Vec<(User, Vec<(Post, Vec<Comment>)>)> = users.into_iter().zip(posts_and_comments).collect();
//! ```
//!
//! And that's it. This module will be expanded in the future with more complex joins, and the
//! ability to define "through" associations (e.g. load all the comments left on any posts written
//! by a user in a single query). However, the goal is to provide simple building blocks which can
//! be used to construct the complex behavior applications need.
mod belongs_to;

use std::hash::Hash;

use query_source::Table;

pub use self::belongs_to::{BelongsTo, GroupedBy};

pub trait HasTable {
    type Table: Table;

    fn table() -> Self::Table;
}

impl<'a, T: HasTable> HasTable for &'a T {
    type Table = T::Table;

    fn table() -> Self::Table {
        T::table()
    }
}

/// Represents a struct which can be identified on a single table in the
/// database. This must be implemented to use associations, and some features of
/// updating. This trait is usually implemented on a reference to a struct, not
/// the struct itself.
///
/// ### Deriving
///
/// This trait can be automatically derived using `diesel_codegen` by adding
/// `#[derive(Identifiable)]` to your struct. The primary key will be assumed to
/// be a field and column called `id`. By default the table will be assumed to
/// be the plural form of the struct name (using *very* dumb pluralization -- it
/// just adds an `s` at the end). If your table name differs from that
/// convention, or requires complex pluralization, it can be specified using
/// `#[table_name = "some_table_name"]`. The inferred table name is considered
/// public API and will never change without a major version bump.
pub trait Identifiable: HasTable {
    type Id: Hash + Eq;

    fn id(self) -> Self::Id;
}
<commit_msg>Add a mention to the foreign_key argument of has_many and belongs_to.<commit_after>// FIXME: Replace these examples with executable tests
//! Traits related to relationships between multiple tables.
//!
//! **Note: This feature is under active development, and we are seeking feedback on the APIs that
//! have been released. Please feel free to [open issues][open-issue], or join [our chat][gitter]
//! to provide feedback.**
//!
//! [open-issue]: https://github.com/diesel-rs/diesel/issues/new
//! [gitter]: https://gitter.im/diesel-rs/diesel
//!
//! Note: The derives in this guide are provided by `diesel_codegen`. Make sure you have
//! `#[macro_use] extern crate diesel_codegen;` at the root of your crate.
//!
//! Associations in Diesel are bidirectional, but primarily focus on the child-to-parent
//! relationship. You can declare an association between two records with `#[has_many]` and
//! `#[belongs_to]`.
//!
//! ```ignore
//! #[derive(Identifiable, Queryable, Associations)]
//! #[has_many(posts)]
//! pub struct User {
//!     id: i32,
//!     name: String,
//! }
//!
//! #[derive(Identifiable, Queryable, Associations)]
//! #[belongs_to(User)]
//! pub struct Post {
//!     id: i32,
//!     user_id: i32,
//!     title: String,
//! }
//! ```
//!
//! Note that in addition to the `#[has_many]` and `#[belongs_to]` annotations, we also need to
//! `#[derive(Associations)]`
//!
//! `#[has_many]` should be passed the name of the table that the children will be loaded from,
//! while `#[belongs_to]` is given the name of the struct that represents the parent. Both types
//! must implement the [`Identifiable`][identifiable] trait. The struct or table referenced in your
//! association has to be in scope, so you'll need `use schema::posts` or similar to bring the
//! table into scope, and `use some_module::User` if `User` were in a different module.
//!
//! [Identifiable]: trait.Identifiable.html
//!
//! If the name of your foreign key doesn't follow the convention `tablename_id`, you can specify a
//! custom one to `#[has_many]` and `#[belongs_to]` by adding a `foreign_key` argument to the
//! attribute like so `#[has_many, foreign_key="mykey"]`.
//!
//! `#[has_many]` actually has no behavior on its own. It only enables joining between the two
//! tables. If you are only writing `children.inner_join(parents)` or
//! `Child::belonging_to(&parents)`, you only need to define the `#[belongs_to]` side.
//!
//! Once the associations are defined, you can join between the two tables using the
//! [`inner_join`][inner-join] or [`left_outer_join`][left-outer-join].
//!
//! [inner-join]: /diesel/query_source/trait.Table.html#method.inner_join
//! [left-outer-join]: /diesel/query_source/trait.Table.html#method.left_outer_join
//!
//! ```ignore
//! let data: Vec<(User, Post)> = users::table.inner_join(posts::table).load(&connection);
//! ```
//!
//! Note: Due to language limitations, only two tables can be joined per query. This will change in
//! the future.
//!
//! Typically however, queries are loaded in multiple queries. For most datasets, the reduced
//! amount of duplicated information sent over the wire saves more time than the extra round trip
//! costs us. You can load the children for a single parent using the
//! [`belonging_to`][belonging-to]
//!
//! [belonging-to]: /diesel/prelude/trait.BelongingToDsl.html#tymethod.belonging_to
//!
//! ```ignore
//! let user = try!(users::find(1).first(&connection));
//! let posts = Post::belonging_to(&user).load(&connection);
//! ```
//!
//! If you're coming from other ORMs, you'll notice that this design is quite different from most.
//! There you would have an instance method on the parent, or have the children stored somewhere on
//! the posts. This design leads to many problems, including [N+1 query
//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an
//! association that isn't there.
//!
//! [load-your-entire-database-into-memory-lol]: http://stackoverflow.com/q/97197/1254484
//!
//! In Diesel, data and its associations are considered to be separate. If you want to pass around
//! a user and all of its posts, that type is `(User, Vec<Post>)`.
//!
//! Next lets look at how to load the children for more than one parent record.
//! [`belonging_to`][belonging-to] can be used to load the data, but we'll also need to group it
//! with its parents. For this we use an additional method [`grouped_by`][grouped-by]
//!
//! [grouped-by]: trait.GroupedBy.html#tymethod.grouped_by
//!
//! ```ignore
//! fn first_twenty_users_and_their_posts(conn: &PgConnection) -> QueryResult<Vec<(User, Vec<Post>)>> {
//!     let users = try!(users::limit(20).load::<User>(conn));
//!     let posts = try!(Post::belonging_to(&users).load::<Post>(conn));
//!     let grouped_posts = posts.grouped_by(&users);
//!     users.into_iter().zip(grouped_posts).collect()
//! }
//! ```
//!
//! [`grouped_by`][grouped-by] takes a `Vec<Child>` and a `Vec<Parent>` and returns a
//! `Vec<Vec<Child>>` where the index of the children matches the index of the parent they belong
//! to. Or to put it another way, it returns them in an order ready to be `zip`ed with the parents. You
//! can do this multiple times. For example, if you wanted to load the comments for all the posts
//! as well, you could do this: (explicit type annotations have been added for documentation
//! purposes)
//!
//! ```ignore
//! let posts: Vec<Post> = try!(Post::belonging_to(&users).load());
//! let comments: Vec<Comment> = try!(Comment::belonging_to(&posts).load());
//! let comments: Vec<Vec<Comment>> = comments.grouped_by(&posts);
//! let posts_and_comments: Vec<Vec<(Post, Vec<Comment>)>> = posts.into_iter().zip(comments).grouped_by(&users);
//! let result: Vec<(User, Vec<(Post, Vec<Comment>)>)> = users.into_iter().zip(posts_and_comments).collect();
//! ```
//!
//! And that's it. This module will be expanded in the future with more complex joins, and the
//! ability to define "through" associations (e.g. load all the comments left on any posts written
//! by a user in a single query). However, the goal is to provide simple building blocks which can
//! be used to construct the complex behavior applications need.
mod belongs_to;

use std::hash::Hash;

use query_source::Table;

pub use self::belongs_to::{BelongsTo, GroupedBy};

pub trait HasTable {
    type Table: Table;

    fn table() -> Self::Table;
}

impl<'a, T: HasTable> HasTable for &'a T {
    type Table = T::Table;

    fn table() -> Self::Table {
        T::table()
    }
}

/// Represents a struct which can be identified on a single table in the
/// database. This must be implemented to use associations, and some features of
/// updating. This trait is usually implemented on a reference to a struct, not
/// the struct itself.
///
/// ### Deriving
///
/// This trait can be automatically derived using `diesel_codegen` by adding
/// `#[derive(Identifiable)]` to your struct. The primary key will be assumed to
/// be a field and column called `id`. By default the table will be assumed to
/// be the plural form of the struct name (using *very* dumb pluralization -- it
/// just adds an `s` at the end). If your table name differs from that
/// convention, or requires complex pluralization, it can be specified using
/// `#[table_name = "some_table_name"]`. The inferred table name is considered
/// public API and will never change without a major version bump.
pub trait Identifiable: HasTable {
    type Id: Hash + Eq;

    fn id(self) -> Self::Id;
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::sync::Arc;

use hir_def::{
    path::{path, Path},
    resolver::HasResolver,
    AdtId, FunctionId,
};
use hir_expand::{diagnostics::DiagnosticSink, name::Name};
use ra_syntax::ast;
use ra_syntax::AstPtr;
use rustc_hash::FxHashSet;

use crate::{
    db::HirDatabase,
    diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr},
    utils::variant_data,
    ApplicationTy, InferenceResult, Ty, TypeCtor,
    _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
};

pub use hir_def::{
    body::{
        scope::{ExprScopes, ScopeEntry, ScopeId},
        Body, BodySourceMap, ExprPtr, ExprSource, PatPtr, PatSource,
    },
    expr::{
        ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
        MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp,
    },
    VariantId,
};

pub struct ExprValidator<'a, 'b: 'a> {
    func: FunctionId,
    infer: Arc<InferenceResult>,
    sink: &'a mut DiagnosticSink<'b>,
}

impl<'a, 'b> ExprValidator<'a, 'b> {
    pub fn new(
        func: FunctionId,
        infer: Arc<InferenceResult>,
        sink: &'a mut DiagnosticSink<'b>,
    ) -> ExprValidator<'a, 'b> {
        ExprValidator { func, infer, sink }
    }

    pub fn validate_body(&mut self, db: &dyn HirDatabase) {
        let body = db.body(self.func.into());

        for e in body.exprs.iter() {
            if let (id, Expr::RecordLit { path, fields, spread }) = e {
                self.validate_record_literal(id, path, fields, *spread, db);
            } else if let (id, Expr::Match { expr, arms }) = e {
                self.validate_match(id, *expr, arms, db, self.infer.clone());
            }
        }

        let body_expr = &body[body.body_expr];
        if let Expr::Block { tail: Some(t), .. } = body_expr {
            self.validate_results_in_tail_expr(body.body_expr, *t, db);
        }
    }

    fn validate_match(
        &mut self,
        id: ExprId,
        match_expr: ExprId,
        arms: &[MatchArm],
        db: &dyn HirDatabase,
        infer: Arc<InferenceResult>,
    ) {
        let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
            db.body_with_source_map(self.func.into());

        let match_expr_ty = match infer.type_of_expr.get(match_expr) {
            Some(ty) => ty,
            // If we can't resolve the type of the match expression
            // we cannot perform exhaustiveness checks.
            None => return,
        };

        let cx = MatchCheckCtx { body, infer: infer.clone(), db };
        let pats = arms.iter().map(|arm| arm.pat);

        let mut seen = Matrix::empty();
        for pat in pats {
            // We skip any patterns whose type we cannot resolve.
            if let Some(pat_ty) = infer.type_of_pat.get(pat) {
                // We only include patterns whose type matches the type
                // of the match expression. If we had a InvalidMatchArmPattern
                // diagnostic or similar we could raise that in an else
                // block here.
                //
                // When comparing the types, we also have to consider that rustc
                // will automatically de-reference the match expression type if
                // necessary.
                if pat_ty == match_expr_ty
                    || match_expr_ty
                        .as_reference()
                        .map(|(match_expr_ty, _)| match_expr_ty == pat_ty)
                        .unwrap_or(false)
                {
                    // If we had a NotUsefulMatchArm diagnostic, we could
                    // check the usefulness of each pattern as we added it
                    // to the matrix here.
                    let v = PatStack::from_pattern(pat);
                    seen.push(&cx, v);
                }
            }
        }

        match is_useful(&cx, &seen, &PatStack::from_wild()) {
            Ok(Usefulness::Useful) => (),
            // if a wildcard pattern is not useful, then all patterns are covered
            Ok(Usefulness::NotUseful) => return,
            // this path is for unimplemented checks, so we err on the side of not
            // reporting any errors
            _ => return,
        }

        if let Ok(source_ptr) = source_map.expr_syntax(id) {
            if let Some(expr) = source_ptr.value.left() {
                let root = source_ptr.file_syntax(db.upcast());
                if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) {
                    if let (Some(match_expr), Some(arms)) =
                        (match_expr.expr(), match_expr.match_arm_list())
                    {
                        self.sink.push(MissingMatchArms {
                            file: source_ptr.file_id,
                            match_expr: AstPtr::new(&match_expr),
                            arms: AstPtr::new(&arms),
                        })
                    }
                }
            }
        }
    }

    fn validate_record_literal(
        &mut self,
        id: ExprId,
        _path: &Option<Path>,
        fields: &[RecordLitField],
        spread: Option<ExprId>,
        db: &dyn HirDatabase,
    ) {
        if spread.is_some() {
            return;
        };
        let variant_def: VariantId = match self.infer.variant_resolution_for_expr(id) {
            Some(VariantId::UnionId(_)) | None => return,
            Some(it) => it,
        };
        if let VariantId::UnionId(_) = variant_def {
            return;
        }

        let variant_data = variant_data(db.upcast(), variant_def);

        let lit_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
        let missed_fields: Vec<Name> = variant_data
            .fields()
            .iter()
            .filter_map(|(_f, d)| {
                let name = d.name.clone();
                if lit_fields.contains(&name) {
                    None
                } else {
                    Some(name)
                }
            })
            .collect();
        if missed_fields.is_empty() {
            return;
        }
        let (_, source_map) = db.body_with_source_map(self.func.into());

        if let Ok(source_ptr) = source_map.expr_syntax(id) {
            if let Some(expr) = source_ptr.value.left() {
                let root = source_ptr.file_syntax(db.upcast());
                if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
                    if let Some(field_list) = record_lit.record_field_list() {
                        self.sink.push(MissingFields {
                            file: source_ptr.file_id,
                            field_list: AstPtr::new(&field_list),
                            missed_fields,
                        })
                    }
                }
            }
        }
    }

    fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) {
        // the mismatch will be on the whole block currently
        let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
            Some(m) => m,
            None => return,
        };

        let std_result_path = path![std::result::Result];

        let resolver = self.func.resolver(db.upcast());
        let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) {
            Some(it) => it,
            _ => return,
        };

        let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum));
        let params = match &mismatch.expected {
            Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
            _ => return,
        };

        if params.len() == 2 && params[0] == mismatch.actual {
            let (_, source_map) = db.body_with_source_map(self.func.into());

            if let Ok(source_ptr) = source_map.expr_syntax(id) {
                if let Some(expr) = source_ptr.value.left() {
                    self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
                }
            }
        }
    }
}
<commit_msg>add fixme to use type checker rather than manually comparing types<commit_after>//! FIXME: write short doc here

use std::sync::Arc;

use hir_def::{
    path::{path, Path},
    resolver::HasResolver,
    AdtId, FunctionId,
};
use hir_expand::{diagnostics::DiagnosticSink, name::Name};
use ra_syntax::ast;
use ra_syntax::AstPtr;
use rustc_hash::FxHashSet;

use crate::{
    db::HirDatabase,
    diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr},
    utils::variant_data,
    ApplicationTy, InferenceResult, Ty, TypeCtor,
    _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
};

pub use hir_def::{
    body::{
        scope::{ExprScopes, ScopeEntry, ScopeId},
        Body, BodySourceMap, ExprPtr, ExprSource, PatPtr, PatSource,
    },
    expr::{
        ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
        MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp,
    },
    VariantId,
};

pub struct ExprValidator<'a, 'b: 'a> {
    func: FunctionId,
    infer: Arc<InferenceResult>,
    sink: &'a mut DiagnosticSink<'b>,
}

impl<'a, 'b> ExprValidator<'a, 'b> {
    pub fn new(
        func: FunctionId,
        infer: Arc<InferenceResult>,
        sink: &'a mut DiagnosticSink<'b>,
    ) -> ExprValidator<'a, 'b> {
        ExprValidator { func, infer, sink }
    }

    pub fn validate_body(&mut self, db: &dyn HirDatabase) {
        let body = db.body(self.func.into());

        for e in body.exprs.iter() {
            if let (id, Expr::RecordLit { path, fields, spread }) = e {
                self.validate_record_literal(id, path, fields, *spread, db);
            } else if let (id, Expr::Match { expr, arms }) = e {
                self.validate_match(id, *expr, arms, db, self.infer.clone());
            }
        }

        let body_expr = &body[body.body_expr];
        if let Expr::Block { tail: Some(t), .. } = body_expr {
            self.validate_results_in_tail_expr(body.body_expr, *t, db);
        }
    }

    fn validate_match(
        &mut self,
        id: ExprId,
        match_expr: ExprId,
        arms: &[MatchArm],
        db: &dyn HirDatabase,
        infer: Arc<InferenceResult>,
    ) {
        let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
            db.body_with_source_map(self.func.into());

        let match_expr_ty = match infer.type_of_expr.get(match_expr) {
            Some(ty) => ty,
            // If we can't resolve the type of the match expression
            // we cannot perform exhaustiveness checks.
            None => return,
        };

        let cx = MatchCheckCtx { body, infer: infer.clone(), db };
        let pats = arms.iter().map(|arm| arm.pat);

        let mut seen = Matrix::empty();
        for pat in pats {
            // We skip any patterns whose type we cannot resolve.
            //
            // This could lead to false positives in this diagnostic, so
            // it might be better to skip the entire diagnostic if we either
            // cannot resolve a match arm or determine that the match arm has
            // the wrong type.
            if let Some(pat_ty) = infer.type_of_pat.get(pat) {
                // We only include patterns whose type matches the type
                // of the match expression. If we had a InvalidMatchArmPattern
                // diagnostic or similar we could raise that in an else
                // block here.
                //
                // When comparing the types, we also have to consider that rustc
                // will automatically de-reference the match expression type if
                // necessary.
                //
                // FIXME we should use the type checker for this.
                if pat_ty == match_expr_ty
                    || match_expr_ty
                        .as_reference()
                        .map(|(match_expr_ty, _)| match_expr_ty == pat_ty)
                        .unwrap_or(false)
                {
                    // If we had a NotUsefulMatchArm diagnostic, we could
                    // check the usefulness of each pattern as we added it
                    // to the matrix here.
                    let v = PatStack::from_pattern(pat);
                    seen.push(&cx, v);
                }
            }
        }

        match is_useful(&cx, &seen, &PatStack::from_wild()) {
            Ok(Usefulness::Useful) => (),
            // if a wildcard pattern is not useful, then all patterns are covered
            Ok(Usefulness::NotUseful) => return,
            // this path is for unimplemented checks, so we err on the side of not
            // reporting any errors
            _ => return,
        }

        if let Ok(source_ptr) = source_map.expr_syntax(id) {
            if let Some(expr) = source_ptr.value.left() {
                let root = source_ptr.file_syntax(db.upcast());
                if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) {
                    if let (Some(match_expr), Some(arms)) =
                        (match_expr.expr(), match_expr.match_arm_list())
                    {
                        self.sink.push(MissingMatchArms {
                            file: source_ptr.file_id,
                            match_expr: AstPtr::new(&match_expr),
                            arms: AstPtr::new(&arms),
                        })
                    }
                }
            }
        }
    }

    fn validate_record_literal(
        &mut self,
        id: ExprId,
        _path: &Option<Path>,
        fields: &[RecordLitField],
        spread: Option<ExprId>,
        db: &dyn HirDatabase,
    ) {
        if spread.is_some() {
            return;
        };
        let variant_def: VariantId = match self.infer.variant_resolution_for_expr(id) {
            Some(VariantId::UnionId(_)) | None => return,
            Some(it) => it,
        };
        if let VariantId::UnionId(_) = variant_def {
            return;
        }

        let variant_data = variant_data(db.upcast(), variant_def);

        let lit_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
        let missed_fields: Vec<Name> = variant_data
            .fields()
            .iter()
            .filter_map(|(_f, d)| {
                let name = d.name.clone();
                if lit_fields.contains(&name) {
                    None
                } else {
                    Some(name)
                }
            })
            .collect();
        if missed_fields.is_empty() {
            return;
        }
        let (_, source_map) = db.body_with_source_map(self.func.into());

        if let Ok(source_ptr) = source_map.expr_syntax(id) {
            if let Some(expr) = source_ptr.value.left() {
                let root = source_ptr.file_syntax(db.upcast());
                if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
                    if let Some(field_list) = record_lit.record_field_list() {
                        self.sink.push(MissingFields {
                            file: source_ptr.file_id,
                            field_list: AstPtr::new(&field_list),
                            missed_fields,
                        })
                    }
                }
            }
        }
    }

    fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) {
        // the mismatch will be on the whole block currently
        let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
            Some(m) => m,
            None => return,
        };

        let std_result_path = path![std::result::Result];

        let resolver = self.func.resolver(db.upcast());
        let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) {
            Some(it) => it,
            _ => return,
        };

        let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum));
        let params = match &mismatch.expected {
            Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
            _ => return,
        };

        if params.len() == 2 && params[0] == mismatch.actual {
            let (_, source_map) = db.body_with_source_map(self.func.into());

            if let Ok(source_ptr) = source_map.expr_syntax(id) {
                if let Some(expr) = source_ptr.value.left() {
                    self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::utils;
use serde::{Deserialize, Serialize};
use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
};

// Lru cache will consume about 2G RAM based on this default value.
pub const DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD: usize = 1 << 13;

pub const TARGET_SNAPSHOT_SIZE: usize = 100_000;

/// Port selected RocksDB options for tuning underlying rocksdb instance of AptosDB.
/// see <https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h>
/// for detailed explanations.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfig {
    pub max_open_files: i32,
    pub max_total_wal_size: u64,
    pub max_background_jobs: i32,
    pub block_cache_size: u64,
    pub block_size: u64,
    pub cache_index_and_filter_blocks: bool,
}

impl Default for RocksdbConfig {
    fn default() -> Self {
        Self {
            // Allow db to close old sst files, saving memory.
            max_open_files: 5000,
            // For now we set the max total WAL size to be 1G. This config can be useful when column
            // families are updated at non-uniform frequencies.
            max_total_wal_size: 1u64 << 30,
            // This includes threads for flashing and compaction. Rocksdb will decide the # of
            // threads to use internally.
            max_background_jobs: 16,
            // Default block cache size is 8MB,
            block_cache_size: 8 * (1u64 << 20),
            // Default block cache size is 4KB,
            block_size: 4 * (1u64 << 10),
            // Whether cache index and filter blocks into block cache.
            cache_index_and_filter_blocks: false,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfigs {
    pub ledger_db_config: RocksdbConfig,
    pub state_merkle_db_config: RocksdbConfig,
    pub index_db_config: RocksdbConfig,
}

impl Default for RocksdbConfigs {
    fn default() -> Self {
        Self {
            ledger_db_config: RocksdbConfig::default(),
            state_merkle_db_config: RocksdbConfig::default(),
            index_db_config: RocksdbConfig {
                max_open_files: 1000,
                ..Default::default()
            },
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageConfig {
    pub backup_service_address: SocketAddr,
    pub dir: PathBuf,
    pub storage_pruner_config: PrunerConfig,
    #[serde(skip)]
    data_dir: PathBuf,
    /// The threshold that determine whether a snapshot should be committed to state merkle db.
    pub target_snapshot_size: usize,
    /// The max # of nodes for a lru cache shard.
    pub max_num_nodes_per_lru_cache_shard: usize,
    /// Rocksdb-specific configurations
    pub rocksdb_configs: RocksdbConfigs,
    /// Try to enable the internal indexer. The indexer expects to have seen all transactions
    /// since genesis. To recover operation after data loss, or to bootstrap a node in fast sync
    /// mode, the indexer db needs to be copied in from another node.
    pub enable_indexer: bool,
}

pub const NO_OP_STORAGE_PRUNER_CONFIG: PrunerConfig = PrunerConfig {
    ledger_pruner_config: LedgerPrunerConfig {
        enable: false,
        prune_window: 0,
        batch_size: 0,
        user_pruning_window_offset: 0,
    },
    state_merkle_pruner_config: StateMerklePrunerConfig {
        enable: false,
        prune_window: 0,
        batch_size: 0,
    },
    epoch_snapshot_pruner_config: EpochSnapshotPrunerConfig {
        enable: false,
        prune_window: 0,
        batch_size: 0,
    },
};

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct LedgerPrunerConfig {
    /// Boolean to enable/disable the ledger pruner. The ledger pruner is responsible for pruning
    /// everything else except for states (e.g. transactions, events etc.)
    pub enable: bool,
    /// This is the default pruning window for any other store except for state store. State store
    /// being big in size, we might want to configure a smaller window for state store vs other
    /// store.
    pub prune_window: u64,
    /// Batch size of the versions to be sent to the ledger pruner - this is to avoid slowdown due to
    /// issuing too many DB calls and batch prune instead. For ledger pruner, this means the number
    /// of versions to prune a time.
    pub batch_size: usize,
    /// The offset for user pruning window to adjust
    pub user_pruning_window_offset: u64,
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StateMerklePrunerConfig {
    /// Boolean to enable/disable the state store pruner. The state pruner is responsible for
    /// pruning state tree nodes.
    pub enable: bool,
    /// The size of the window should be calculated based on disk space availability and system TPS.
    pub prune_window: u64,
    /// Similar to the variable above but for state store pruner. It means the number of stale
    /// nodes to prune a time.
    pub batch_size: usize,
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct EpochSnapshotPrunerConfig {
    pub enable: bool,
    /// Window size in versions, but only the snapshots at epoch ending versions are kept, because
    /// other snapshots are pruned by the state merkle pruner.
    pub prune_window: u64,
    /// Number of stale nodes to prune a time.
    pub batch_size: usize,
}

// Config for the epoch ending state pruner is actually in the same format as the state merkle
// pruner, but it has it's own type hence separate default values. This converts it to the same
// type, to use the same pruner implementation (but parameterized on the stale node index DB schema).
impl From<EpochSnapshotPrunerConfig> for StateMerklePrunerConfig {
    fn from(config: EpochSnapshotPrunerConfig) -> Self {
        Self {
            enable: config.enable,
            prune_window: config.prune_window,
            batch_size: config.batch_size,
        }
    }
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, Default)]
#[serde(default, deny_unknown_fields)]
pub struct PrunerConfig {
    pub ledger_pruner_config: LedgerPrunerConfig,
    pub state_merkle_pruner_config: StateMerklePrunerConfig,
    pub epoch_snapshot_pruner_config: EpochSnapshotPrunerConfig,
}

impl Default for LedgerPrunerConfig {
    fn default() -> Self {
        LedgerPrunerConfig {
            enable: true,
            // This assumes we have 1T disk, minus the space needed by state merkle db and the
            // overhead in storage.
            prune_window: 150_000_000,
            batch_size: 500,
            user_pruning_window_offset: 200_000,
        }
    }
}

impl Default for StateMerklePrunerConfig {
    fn default() -> Self {
        StateMerklePrunerConfig {
            enable: true,
            // This allows a block / chunk being executed to have access to a non-latest state tree.
            // It needs to be greater than the number of versions the state committing thread is
            // able to commit during the execution of the block / chunk.
            prune_window: 100_000,
            // A 10k transaction block (touching 60k state values, in the case of the account
            // creation benchmark) on a 4B items DB (or 1.33B accounts) yields 300k JMT nodes
            batch_size: 1_000,
        }
    }
}

impl Default for EpochSnapshotPrunerConfig {
    fn default() -> Self {
        Self {
            enable: true,
            // This is based on ~5K TPS * 2h/epoch * 2 epochs. -- epoch ending snapshots are used
            // by state sync in fast sync mode.
            // The setting is in versions, not epochs, because this makes it behave more like other
            // pruners: a slower network will have longer history in db with the same pruner
            // settings, but the disk space take will be similar.
            // settings.
            prune_window: 80_000_000,
            // A 10k transaction block (touching 60k state values, in the case of the account
            // creation benchmark) on a 4B items DB (or 1.33B accounts) yields 300k JMT nodes
            batch_size: 1_000,
        }
    }
}

impl Default for StorageConfig {
    fn default() -> StorageConfig {
        StorageConfig {
            backup_service_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6186),
            dir: PathBuf::from("db"),
            // The prune window must at least out live a RPC request because its sub requests are
            // to return a consistent view of the DB at exactly same version. Considering a few
            // thousand TPS we are potentially going to achieve, and a few minutes a consistent view
            // of the DB might require, 10k (TPS)  * 100 (seconds)  =  1 Million might be a
            // conservatively safe minimal prune window. It'll take a few Gigabytes of disk space
            // depending on the size of an average account blob.
            storage_pruner_config: PrunerConfig::default(),
            data_dir: PathBuf::from("/opt/aptos/data"),
            rocksdb_configs: RocksdbConfigs::default(),
            enable_indexer: false,
            target_snapshot_size: TARGET_SNAPSHOT_SIZE,
            max_num_nodes_per_lru_cache_shard: DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD,
        }
    }
}

impl StorageConfig {
    pub fn dir(&self) -> PathBuf {
        if self.dir.is_relative() {
            self.data_dir.join(&self.dir)
        } else {
            self.dir.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }

    pub fn randomize_ports(&mut self) {
        self.backup_service_address
            .set_port(utils::get_available_port());
    }
}

#[cfg(test)]
mod test {
    use crate::config::PrunerConfig;

    #[test]
    pub fn tset_default_prune_window() {
        // Not that these can't be changed, but think twice -- make them safe for mainnet

        let config = PrunerConfig::default();
        assert!(config.ledger_pruner_config.prune_window >= 50_000_000);
        assert!(config.state_merkle_pruner_config.prune_window >= 100_000);
        assert!(config.epoch_snapshot_pruner_config.prune_window > 50_000_000);
    }
}
<commit_msg>[storage] larger default state prune window<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::utils;
use serde::{Deserialize, Serialize};
use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
};

// Lru cache will consume about 2G RAM based on this default value.
pub const DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD: usize = 1 << 13;

pub const TARGET_SNAPSHOT_SIZE: usize = 100_000;

/// Port selected RocksDB options for tuning underlying rocksdb instance of AptosDB.
/// see <https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h>
/// for detailed explanations.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfig {
    pub max_open_files: i32,
    pub max_total_wal_size: u64,
    pub max_background_jobs: i32,
    pub block_cache_size: u64,
    pub block_size: u64,
    pub cache_index_and_filter_blocks: bool,
}

impl Default for RocksdbConfig {
    fn default() -> Self {
        Self {
            // Allow db to close old sst files, saving memory.
            max_open_files: 5000,
            // For now we set the max total WAL size to be 1G. This config can be useful when column
            // families are updated at non-uniform frequencies.
            max_total_wal_size: 1u64 << 30,
            // This includes threads for flashing and compaction. Rocksdb will decide the # of
            // threads to use internally.
            max_background_jobs: 16,
            // Default block cache size is 8MB,
            block_cache_size: 8 * (1u64 << 20),
            // Default block cache size is 4KB,
            block_size: 4 * (1u64 << 10),
            // Whether cache index and filter blocks into block cache.
            cache_index_and_filter_blocks: false,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfigs {
    pub ledger_db_config: RocksdbConfig,
    pub state_merkle_db_config: RocksdbConfig,
    pub index_db_config: RocksdbConfig,
}

impl Default for RocksdbConfigs {
    fn default() -> Self {
        Self {
            ledger_db_config: RocksdbConfig::default(),
            state_merkle_db_config: RocksdbConfig::default(),
            index_db_config: RocksdbConfig {
                max_open_files: 1000,
                ..Default::default()
            },
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageConfig {
    pub backup_service_address: SocketAddr,
    pub dir: PathBuf,
    pub storage_pruner_config: PrunerConfig,
    #[serde(skip)]
    data_dir: PathBuf,
    /// The threshold that determine whether a snapshot should be committed to state merkle db.
    pub target_snapshot_size: usize,
    /// The max # of nodes for a lru cache shard.
    pub max_num_nodes_per_lru_cache_shard: usize,
    /// Rocksdb-specific configurations
    pub rocksdb_configs: RocksdbConfigs,
    /// Try to enable the internal indexer. The indexer expects to have seen all transactions
    /// since genesis. To recover operation after data loss, or to bootstrap a node in fast sync
    /// mode, the indexer db needs to be copied in from another node.
    pub enable_indexer: bool,
}

pub const NO_OP_STORAGE_PRUNER_CONFIG: PrunerConfig = PrunerConfig {
    ledger_pruner_config: LedgerPrunerConfig {
        enable: false,
        prune_window: 0,
        batch_size: 0,
        user_pruning_window_offset: 0,
    },
    state_merkle_pruner_config: StateMerklePrunerConfig {
        enable: false,
        prune_window: 0,
        batch_size: 0,
    },
    epoch_snapshot_pruner_config: EpochSnapshotPrunerConfig {
        enable: false,
        prune_window: 0,
        batch_size: 0,
    },
};

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct LedgerPrunerConfig {
    /// Boolean to enable/disable the ledger pruner. The ledger pruner is responsible for pruning
    /// everything else except for states (e.g. transactions, events etc.)
    pub enable: bool,
    /// This is the default pruning window for any other store except for state store. State store
    /// being big in size, we might want to configure a smaller window for state store vs other
    /// store.
    pub prune_window: u64,
    /// Batch size of the versions to be sent to the ledger pruner - this is to avoid slowdown due to
    /// issuing too many DB calls and batch prune instead. For ledger pruner, this means the number
    /// of versions to prune a time.
    pub batch_size: usize,
    /// The offset for user pruning window to adjust
    pub user_pruning_window_offset: u64,
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StateMerklePrunerConfig {
    /// Boolean to enable/disable the state store pruner. The state pruner is responsible for
    /// pruning state tree nodes.
    pub enable: bool,
    /// The size of the window should be calculated based on disk space availability and system TPS.
    pub prune_window: u64,
    /// Similar to the variable above but for state store pruner. It means the number of stale
    /// nodes to prune a time.
    pub batch_size: usize,
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct EpochSnapshotPrunerConfig {
    pub enable: bool,
    /// Window size in versions, but only the snapshots at epoch ending versions are kept, because
    /// other snapshots are pruned by the state merkle pruner.
    pub prune_window: u64,
    /// Number of stale nodes to prune a time.
    pub batch_size: usize,
}

// Config for the epoch ending state pruner is actually in the same format as the state merkle
// pruner, but it has it's own type hence separate default values. This converts it to the same
// type, to use the same pruner implementation (but parameterized on the stale node index DB schema).
impl From<EpochSnapshotPrunerConfig> for StateMerklePrunerConfig {
    fn from(config: EpochSnapshotPrunerConfig) -> Self {
        Self {
            enable: config.enable,
            prune_window: config.prune_window,
            batch_size: config.batch_size,
        }
    }
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, Default)]
#[serde(default, deny_unknown_fields)]
pub struct PrunerConfig {
    pub ledger_pruner_config: LedgerPrunerConfig,
    pub state_merkle_pruner_config: StateMerklePrunerConfig,
    pub epoch_snapshot_pruner_config: EpochSnapshotPrunerConfig,
}

impl Default for LedgerPrunerConfig {
    fn default() -> Self {
        LedgerPrunerConfig {
            enable: true,
            // This assumes we have 1T disk, minus the space needed by state merkle db and the
            // overhead in storage.
            prune_window: 150_000_000,
            batch_size: 500,
            user_pruning_window_offset: 200_000,
        }
    }
}

impl Default for StateMerklePrunerConfig {
    fn default() -> Self {
        StateMerklePrunerConfig {
            enable: true,
            // This allows a block / chunk being executed to have access to a non-latest state tree.
            // It needs to be greater than the number of versions the state committing thread is
            // able to commit during the execution of the block / chunk. If the bad case indeed
            // happens due to this being too small, a node restart should recover it.
            // Still, defaulting to 1M to be super safe.
            prune_window: 1_000_000,
            // A 10k transaction block (touching 60k state values, in the case of the account
            // creation benchmark) on a 4B items DB (or 1.33B accounts) yields 300k JMT nodes
            batch_size: 1_000,
        }
    }
}

impl Default for EpochSnapshotPrunerConfig {
    fn default() -> Self {
        Self {
            enable: true,
            // This is based on ~5K TPS * 2h/epoch * 2 epochs. -- epoch ending snapshots are used
            // by state sync in fast sync mode.
            // The setting is in versions, not epochs, because this makes it behave more like other
            // pruners: a slower network will have longer history in db with the same pruner
            // settings, but the disk space take will be similar.
            // settings.
            prune_window: 80_000_000,
            // A 10k transaction block (touching 60k state values, in the case of the account
            // creation benchmark) on a 4B items DB (or 1.33B accounts) yields 300k JMT nodes
            batch_size: 1_000,
        }
    }
}

impl Default for StorageConfig {
    fn default() -> StorageConfig {
        StorageConfig {
            backup_service_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6186),
            dir: PathBuf::from("db"),
            // The prune window must at least out live a RPC request because its sub requests are
            // to return a consistent view of the DB at exactly same version. Considering a few
            // thousand TPS we are potentially going to achieve, and a few minutes a consistent view
            // of the DB might require, 10k (TPS)  * 100 (seconds)  =  1 Million might be a
            // conservatively safe minimal prune window. It'll take a few Gigabytes of disk space
            // depending on the size of an average account blob.
            storage_pruner_config: PrunerConfig::default(),
            data_dir: PathBuf::from("/opt/aptos/data"),
            rocksdb_configs: RocksdbConfigs::default(),
            enable_indexer: false,
            target_snapshot_size: TARGET_SNAPSHOT_SIZE,
            max_num_nodes_per_lru_cache_shard: DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD,
        }
    }
}

impl StorageConfig {
    pub fn dir(&self) -> PathBuf {
        if self.dir.is_relative() {
            self.data_dir.join(&self.dir)
        } else {
            self.dir.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }

    pub fn randomize_ports(&mut self) {
        self.backup_service_address
            .set_port(utils::get_available_port());
    }
}

#[cfg(test)]
mod test {
    use crate::config::PrunerConfig;

    #[test]
    pub fn tset_default_prune_window() {
        // Not that these can't be changed, but think twice -- make them safe for mainnet

        let config = PrunerConfig::default();
        assert!(config.ledger_pruner_config.prune_window >= 50_000_000);
        assert!(config.state_merkle_pruner_config.prune_window >= 100_000);
        assert!(config.epoch_snapshot_pruner_config.prune_window > 50_000_000);
    }
}
<|endoftext|>