#![deny(missing_docs)]
#![allow(clippy::enum_variant_names, clippy::result_unit_err)]
use log::{debug, trace};
use nom::*;
use strum_macros::*;
use nom::IResult::*;
use nom::{IResult, Needed};
use std::borrow::Cow;
use std::cmp::min;
use std::convert::From;
use std::io::{self, Read, Write};
use std::ops::Range;
use std::str::{self, FromStr};
mod fs;
#[cfg(feature = "libc")]
mod libc_fs;
mod sigs;
pub use self::fs::{FileSystem, HostErrno, HostMode, HostOpenFlags, IOResult};
#[cfg(feature = "libc")]
pub use self::libc_fs::LibcFS;
pub use self::sigs::Signal;
use self::fs::{write_stat, HostStat};
const MAX_PACKET_SIZE: usize = 64 * 1024;
named!(checksum<&[u8], u8>,
       map_res!(map_res!(take!(2), str::from_utf8),
                |s| u8::from_str_radix(s, 16)));
named!(packet<&[u8], (Vec<u8>, u8)>,
       preceded!(tag!("$"),
                 separated_pair!(map!(opt!(is_not!("#")), |o: Option<&[u8]>| {
                     o.map_or(vec!(), |s| s.to_vec())
                 }),
                                 tag!("#"),
                                 checksum)));
#[derive(Debug, PartialEq, Eq)]
enum Packet {
    Ack,
    Nack,
    Interrupt,
    Data(Vec<u8>, u8),
}
named!(
    packet_or_response<Packet>,
    alt!(
    packet => { |(d, chk)| Packet::Data(d, chk) }
    | tag!("+") => { |_| Packet::Ack }
    | tag!("-") => { |_| Packet::Nack }
    | tag!("\x03") => { |_| Packet::Interrupt }
    )
);
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, EnumString, PartialEq)]
enum GDBFeature {
    multiprocess,
    xmlRegisters,
    qRelocInsn,
    swbreak,
    hwbreak,
    #[strum(serialize = "fork-events")]
    fork_events,
    #[strum(serialize = "vfork-events")]
    vfork_events,
    #[strum(serialize = "exec-events")]
    exec_events,
    vContSupported,
    #[strum(serialize = "no-resumed")]
    no_resumed,
    QThreadEvents,
}
#[derive(Clone, Debug, PartialEq)]
enum Known<'a> {
    Yes(GDBFeature),
    No(&'a str),
}
#[derive(Clone, Debug, PartialEq)]
struct GDBFeatureSupported<'a>(Known<'a>, FeatureSupported<'a>);
#[derive(Clone, Debug, PartialEq)]
enum FeatureSupported<'a> {
    Yes,
    No,
    #[allow(unused)]
    Maybe,
    Value(&'a str),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SetThreadFor {
    Continue,
    ReadRegister,
}
#[derive(Clone, Debug, PartialEq)]
enum Query<'a> {
    Attached(Option<u64>),
    CurrentThread,
    SearchMemory { address: u64, length: u64, bytes: Vec<u8> },
    SupportedFeatures(Vec<GDBFeatureSupported<'a>>),
    StartNoAckMode,
    Invoke(Vec<u8>),
    AddressRandomization(bool),
    CatchSyscalls(Option<Vec<u64>>),
    PassSignals(Vec<u64>),
    ProgramSignals(Vec<u64>),
    ThreadInfo(ThreadId),
    ThreadList(bool),
    ReadBytes {
        object: String,
        annex: String,
        offset: u64,
        length: u64,
    },
    #[cfg(feature = "lldb")]
    RegisterInfo(u64),
    #[cfg(feature = "lldb")]
    ProcessInfo,
    Symbol(String, String),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Id {
    Id(u32),
    All,
    Any,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ThreadId {
    pub pid: Id,
    pub tid: Id,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Watchpoint {
    pub addr: u64,
    pub n_bytes: u64,
}
impl Watchpoint {
    fn new(addr: u64, n_bytes: u64) -> Watchpoint {
        Watchpoint { addr, n_bytes }
    }
}
#[derive(Clone, Debug, PartialEq)]
pub struct Bytecode {
    pub bytecode: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Breakpoint {
    pub addr: u64,
    pub kind: u64,
    pub conditions: Option<Vec<Bytecode>>,
    pub commands: Option<Vec<Bytecode>>,
}
impl Breakpoint {
    fn new(addr: u64, kind: u64, conditions: Option<Vec<Bytecode>>, commands: Option<Vec<Bytecode>>) -> Breakpoint {
        Breakpoint {
            addr,
            kind,
            conditions,
            commands,
        }
    }
}
#[derive(Clone, Debug, PartialEq)]
pub struct MemoryRegion {
    pub address: u64,
    pub length: u64,
}
impl MemoryRegion {
    fn new(address: u64, length: u64) -> MemoryRegion {
        MemoryRegion { address, length }
    }
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VContFeature {
    Continue = b'c',
    ContinueWithSignal = b'C',
    Step = b's',
    StepWithSignal = b'S',
    Stop = b't',
    RangeStep = b'r',
}
#[derive(Clone, Debug, PartialEq)]
pub enum VCont {
    Continue,
    ContinueWithSignal(u8),
    Step,
    StepWithSignal(u8),
    Stop,
    RangeStep(Range<u64>),
}
#[derive(Clone, Debug, PartialEq)]
enum Command<'a> {
    Detach(Option<u64>),
    EnableExtendedMode,
    TargetHaltReason,
    ReadGeneralRegisters,
    WriteGeneralRegisters(Vec<u8>),
    ReadRegister(u64),
    WriteRegister(u64, Vec<u8>),
    Kill(Option<u64>),
    ReadMemory(MemoryRegion),
    WriteMemory(MemoryRegion, Vec<u8>),
    Query(Query<'a>),
    #[cfg(feature = "all_stop")]
    Continue,
    #[cfg(feature = "all_stop")]
    Step,
    Reset,
    PingThread(ThreadId),
    CtrlC,
    UnknownVCommand,
    SetCurrentThread(SetThreadFor, ThreadId),
    InsertSoftwareBreakpoint(Breakpoint),
    InsertHardwareBreakpoint(Breakpoint),
    InsertWriteWatchpoint(Watchpoint),
    InsertReadWatchpoint(Watchpoint),
    InsertAccessWatchpoint(Watchpoint),
    RemoveSoftwareBreakpoint(Breakpoint),
    RemoveHardwareBreakpoint(Breakpoint),
    RemoveWriteWatchpoint(Watchpoint),
    RemoveReadWatchpoint(Watchpoint),
    RemoveAccessWatchpoint(Watchpoint),
    VContSupported,
    VCont(Vec<(VCont, Option<ThreadId>)>),
    HostOpen(Vec<u8>, u64, u64),
    HostClose(u64),
    HostPRead(u64, u64, u64),
    HostPWrite(u64, u64, Vec<u8>),
    HostFStat(u64),
    HostUnlink(Vec<u8>),
    HostReadlink(Vec<u8>),
    HostSetFS(u64),
}
named!(
    gdbfeature<Known<'_>>,
    map!(map_res!(is_not_s!(";="), str::from_utf8), |s| {
        match GDBFeature::from_str(s) {
            Ok(f) => Known::Yes(f),
            Err(_) => Known::No(s),
        }
    })
);
fn gdbfeaturesupported<'a>(i: &'a [u8]) -> IResult<&'a [u8], GDBFeatureSupported<'a>> {
    flat_map!(i, is_not!(";"), |f: &'a [u8]| {
        match f.split_last() {
            None => IResult::Incomplete(Needed::Size(2)),
            Some((&b'+', first)) => map!(first, gdbfeature, |feat| GDBFeatureSupported(
                feat,
                FeatureSupported::Yes
            )),
            Some((&b'-', first)) => map!(first, gdbfeature, |feat| GDBFeatureSupported(
                feat,
                FeatureSupported::No
            )),
            Some((_, _)) => map!(
                f,
                separated_pair!(gdbfeature, tag!("="), map_res!(is_not!(";"), str::from_utf8)),
                |(feat, value)| GDBFeatureSupported(feat, FeatureSupported::Value(value))
            ),
        }
    })
}
named!(q_search_memory<&[u8], (u64, u64, Vec<u8>)>,
       complete!(do_parse!(
           tag!("qSearch:memory:") >>
           address: hex_value >>
           tag!(";") >>
           length: hex_value >>
           tag!(";") >>
           data: hex_byte_sequence >>
           (address, length, data))));
#[cfg(feature = "lldb")]
fn lldb_query(i: &[u8]) -> IResult<&[u8], Query<'_>> {
    alt!(i,
        tag!("qProcessInfo") => { |_| Query::ProcessInfo }
        | preceded!(tag!("qRegisterInfo"), hex_value) => {
            |reg| Query::RegisterInfo(reg)
        }
    )
}
#[cfg(not(feature = "lldb"))]
fn lldb_query(_i: &[u8]) -> IResult<&[u8], Query<'_>> {
    IResult::Error(error_position!(ErrorKind::Alt, _i))
}
fn query<'a>(i: &'a [u8]) -> IResult<&'a [u8], Query<'a>> {
    alt_complete!(i,
    tag!("qC") => { |_| Query::CurrentThread }
    | preceded!(tag!("qSupported"),
                preceded!(tag!(":"),
                          separated_list_complete!(tag!(";"),
                                                   gdbfeaturesupported))) => {
        |features: Vec<GDBFeatureSupported<'a>>| Query::SupportedFeatures(features)
    }
    | preceded!(tag!("qRcmd,"), hex_byte_sequence) => {
        |bytes| Query::Invoke(bytes)
    }
    | q_search_memory => {
        |(address, length, bytes)| Query::SearchMemory { address, length, bytes }
    }
    | tag!("QStartNoAckMode") => { |_| Query::StartNoAckMode }
    | preceded!(tag!("qAttached:"), hex_value) => {
        |value| Query::Attached(Some(value))
    }
    | tag!("qAttached") => { |_| Query::Attached(None) }
    | tag!("qfThreadInfo") => { |_| Query::ThreadList(true) }
    | tag!("qsThreadInfo") => { |_| Query::ThreadList(false) }
    | tag!("QDisableRandomization:0") => { |_| Query::AddressRandomization(true) }
    | tag!("QDisableRandomization:1") => { |_| Query::AddressRandomization(false) }
    | tag!("QCatchSyscalls:0") => { |_| Query::CatchSyscalls(None) }
    | preceded!(tag!("QCatchSyscalls:1"),
                many0!(preceded!(tag!(";"), hex_value))) => {
        |syscalls| Query::CatchSyscalls(Some(syscalls))
    }
    | preceded!(tag!("QPassSignals:"),
                separated_list_complete!(tag!(";"), hex_value)) => {
        |signals| Query::PassSignals(signals)
    }
    | preceded!(tag!("QProgramSignals:"),
                separated_nonempty_list_complete!(tag!(";"), hex_value)) => {
        |signals| Query::ProgramSignals(signals)
    }
    | preceded!(tag!("qThreadExtraInfo,"), parse_thread_id) => {
        |thread_id| Query::ThreadInfo(thread_id)
    }
    | tuple!(
        preceded!(tag!("qSymbol:"), map_res!(take_until!(":"), str::from_utf8)),
        preceded!(tag!(":"), map_res!(eof!(), str::from_utf8))) => {
        |(sym_value, sym_name): (&str, &str)| Query::Symbol(sym_value.to_owned(), sym_name.to_owned())
    }
    | tuple!(
        preceded!(tag!("qXfer:"), map_res!(take_until!(":"), str::from_utf8)),
        preceded!(tag!(":read:"), map_res!(take_until!(":"), str::from_utf8)),
        preceded!(tag!(":"), hex_value),
        preceded!(tag!(","), hex_value)) => {
        |(object, annex, offset, length): (&str, &str, u64, u64)| Query::ReadBytes {
            object: object.to_owned(),
            annex: annex.to_owned(),
            offset,
            length,
        }
    }
    | lldb_query => {
        |q| q
    }
    )
}
named!(hex_value<&[u8], u64>,
map!(take_while1!(&nom::is_hex_digit),
     |hex| {
         let s = str::from_utf8(hex).unwrap();
         let r = u64::from_str_radix(s, 16);
         r.unwrap()
     }));
named!(hex_digit<&[u8], char>,
       one_of!("0123456789abcdefABCDEF"));
named!(hex_byte<&[u8], u8>,
       do_parse!(
           digit0: hex_digit >>
           digit1: hex_digit >>
           ((16 * digit0.to_digit(16).unwrap() + digit1.to_digit(16).unwrap()) as u8)
       )
);
named!(hex_byte_sequence<&[u8], Vec<u8>>,
       many1!(hex_byte));
named!(write_memory<&[u8], (u64, u64, Vec<u8>)>,
       complete!(do_parse!(
           tag!("M") >>
           address: hex_value >>
           tag!(",") >>
           length: hex_value >>
           tag!(":") >>
           data: hex_byte_sequence >>
           (address, length, data))));
named!(binary_byte<&[u8], u8>,
       alt_complete!(
           preceded!(tag!("}"), take!(1)) => { |b: &[u8]| b[0] ^ 0x20 } |
           take!(1) => { |b: &[u8]| b[0] }));
named!(binary_byte_sequence<&[u8], Vec<u8>>,
       many1!(binary_byte));
named!(write_memory_binary<&[u8], (u64, u64, Vec<u8>)>,
       complete!(do_parse!(
           tag!("X") >>
           address: hex_value >>
           tag!(",") >>
           length: hex_value >>
           tag!(":") >>
           data: binary_byte_sequence >>
           (address, length, data))));
named!(read_memory<&[u8], (u64, u64)>,
       preceded!(tag!("m"),
                 separated_pair!(hex_value,
                                 tag!(","),
                                 hex_value)));
named!(read_register<&[u8], u64>,
       preceded!(tag!("p"), hex_value));
named!(write_register<&[u8], (u64, Vec<u8>)>,
       preceded!(tag!("P"),
                 separated_pair!(hex_value,
                                 tag!("="),
                                 hex_byte_sequence)));
named!(write_general_registers<&[u8], Vec<u8>>,
       preceded!(tag!("G"), hex_byte_sequence));
named!(parse_thread_id_element<&[u8], Id>,
       alt_complete!(tag!("0") => { |_| Id::Any }
                     | tag!("-1") => { |_| Id::All }
                     | hex_value => { |val: u64| Id::Id(val as u32) }));
named!(parse_thread_id<&[u8], ThreadId>,
alt_complete!(parse_thread_id_element => { |pid| ThreadId { pid, tid: Id::Any } }
              | preceded!(tag!("p"),
                          separated_pair!(parse_thread_id_element,
                                          tag!("."),
                                          parse_thread_id_element)) => {
                  |pair: (Id, Id)| ThreadId { pid: pair.0, tid: pair.1 }
              }
              | preceded!(tag!("p"), parse_thread_id_element) => {
                  |id: Id| ThreadId { pid: id, tid: Id::All }
              }));
named!(parse_ping_thread<&[u8], ThreadId>,
       preceded!(tag!("T"), parse_thread_id));
fn parse_vfile_open(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("open:")
            >> filename: hex_byte_sequence
            >> tag!(",")
            >> flags: hex_value
            >> tag!(",")
            >> mode: hex_value
            >> (Command::HostOpen(filename, flags, mode))
    )
}
fn parse_vfile_close(i: &[u8]) -> IResult<&[u8], Command> {
    do_parse!(
        i,
        tag!("close:") >> fd: hex_value >> (Command::HostClose(fd))
    )
}
fn parse_vfile_pread(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("pread:")
            >> fd: hex_value
            >> tag!(",")
            >> count: hex_value
            >> tag!(",")
            >> offset: hex_value
            >> (Command::HostPRead(fd, count, offset))
    )
}
fn parse_vfile_pwrite(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("pwrite:")
            >> fd: hex_value
            >> tag!(",")
            >> offset: hex_value
            >> tag!(",")
            >> data: binary_byte_sequence
            >> (Command::HostPWrite(fd, offset, data))
    )
}
fn parse_vfile_fstat(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("fstat:") >> fd: hex_value >> (Command::HostFStat(fd))
    )
}
fn parse_vfile_unlink(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("unlink:") >> filename: hex_byte_sequence >> (Command::HostUnlink(filename))
    )
}
fn parse_vfile_readlink(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("readlink:") >> filename: hex_byte_sequence >> (Command::HostReadlink(filename))
    )
}
fn parse_vfile_setfs(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    do_parse!(
        i,
        tag!("setfs:") >> pid: hex_value >> (Command::HostSetFS(pid))
    )
}
fn parse_unknown_vfile_op(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    map!(i, take_till!(|_| { false }), { |_: &[u8]| Command::UnknownVCommand })
}
fn parse_vfile_op(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    alt_complete!(
        i,
        parse_vfile_open
            | parse_vfile_close
            | parse_vfile_pread
            | parse_vfile_pwrite
            | parse_vfile_fstat
            | parse_vfile_unlink
            | parse_vfile_readlink
            | parse_vfile_setfs
            | parse_unknown_vfile_op
    )
}
fn v_command(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    alt_complete!(i,
    tag!("vCtrlC") => { |_| Command::CtrlC }
    | preceded!(tag!("vCont"),
                alt_complete!(tag!("?") => { |_| Command::VContSupported }
                              | many0!(do_parse!(
                                  tag!(";") >>
                                  action: alt_complete!(tag!("c") => { |_| VCont::Continue }
                                                        | preceded!(tag!("C"), hex_byte) => { |sig| VCont::ContinueWithSignal(sig) }
                                                        | tag!("s") => { |_| VCont::Step }
                                                        | preceded!(tag!("S"), hex_byte) => { |sig| VCont::StepWithSignal(sig) }
                                                        | tag!("t") => { |_| VCont::Stop }
                                                        | do_parse!(tag!("r") >>
                                                                    start: hex_value >>
                                                                    tag!(",") >>
                                                                    end: hex_value >>
                                                                    (start, end)) => { |(start, end)| VCont::RangeStep(start..end) }
                                  ) >>
                                  thread: opt!(complete!(preceded!(tag!(":"), parse_thread_id))) >>
                                  (action, thread)
                              )) => { |actions| Command::VCont(actions) }
                )) => {
        |c| c
    }
    | preceded!(tag!("vKill;"), hex_value) => {
        |pid| Command::Kill(Some(pid))
    }
    | preceded!(tag!("vFile:"), parse_vfile_op) => {
        |c| c
    }
    | preceded!(tag!("v"), take_till!(|_| { false })) => {
        |_| Command::UnknownVCommand
    })
}
named!(parse_h_packet<&[u8], (SetThreadFor, ThreadId)>,
    alt_complete!(
       preceded!(tag!("Hg"), parse_thread_id) => {
        |id| (SetThreadFor::ReadRegister, id)
       }
       | preceded!(tag!("Hc"), parse_thread_id) => {
        |id| (SetThreadFor::Continue, id)
       }
));
named!(parse_d_packet<&[u8], Option<u64>>,
       alt_complete!(preceded!(tag!("D;"), hex_value) => {
           |pid| Some(pid)
       }
       | tag!("D") => { |_| None }));
#[derive(Copy, Clone)]
enum ZAction {
    Insert,
    Remove,
}
named!(parse_z_action<&[u8], ZAction>,
       alt_complete!(tag!("z") => { |_| ZAction::Remove } |
                     tag!("Z") => { |_| ZAction::Insert }));
#[derive(Copy, Clone)]
enum ZType {
    SoftwareBreakpoint,
    HardwareBreakpoint,
    WriteWatchpoint,
    ReadWatchpoint,
    AccessWatchpoint,
}
named!(parse_z_type<&[u8], ZType>,
       alt_complete!(tag!("0") => { |_| ZType::SoftwareBreakpoint } |
                     tag!("1") => { |_| ZType::HardwareBreakpoint } |
                     tag!("2") => { |_| ZType::WriteWatchpoint } |
                     tag!("3") => { |_| ZType::ReadWatchpoint } |
                     tag!("4") => { |_| ZType::AccessWatchpoint }));
named!(parse_cond_or_command_expression<&[u8], Bytecode>,
       do_parse!(tag!("X") >>
                 len: hex_value >>
                 tag!(",") >>
                 expr: take!(len) >>
                 (Bytecode { bytecode: expr.to_vec() })));
named!(parse_condition_list<&[u8], Vec<Bytecode>>,
       do_parse!(tag!(";") >>
                 list: many1!(parse_cond_or_command_expression) >>
                 (list)));
fn maybe_condition_list(i: &[u8]) -> IResult<&[u8], Option<Vec<Bytecode>>> {
    match parse_condition_list(i) {
        Done(rest, v) => Done(rest, Some(v)),
        Incomplete(_i) => Done(i, None),
        Error(_) => Done(i, None),
    }
}
named!(parse_command_list<&[u8], Vec<Bytecode>>,
       do_parse!(tag!(";cmds") >>
                 list: alt_complete!(do_parse!(persist_flag: hex_value >>
                                               tag!(",") >>
                                               cmd_list: many1!(parse_cond_or_command_expression) >>
                                               (cmd_list)) |
                                     many1!(parse_cond_or_command_expression)) >>
                 (list)));
fn maybe_command_list(i: &[u8]) -> IResult<&[u8], Option<Vec<Bytecode>>> {
    match parse_command_list(i) {
        Done(rest, v) => Done(rest, Some(v)),
        Incomplete(_i) => Done(i, None),
        Error(e) => Error(e),
    }
}
named!(parse_cond_and_command_list<&[u8], (Option<Vec<Bytecode>>,
                                           Option<Vec<Bytecode>>)>,
       do_parse!(cond_list: maybe_condition_list >>
                 cmd_list: maybe_command_list >>
                 (cond_list, cmd_list)));
fn parse_z_packet(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    let (rest, (action, type_, addr, kind)) = try_parse!(
        i,
        do_parse!(
            action: parse_z_action
                >> type_: parse_z_type
                >> tag!(",")
                >> addr: hex_value
                >> tag!(",")
                >> kind: hex_value
                >> (action, type_, addr, kind)
        )
    );
    return match action {
        ZAction::Insert => insert_command(rest, type_, addr, kind),
        ZAction::Remove => Done(rest, remove_command(type_, addr, kind)),
    };
    fn insert_command(rest: &[u8], type_: ZType, addr: u64, kind: u64) -> IResult<&[u8], Command<'_>> {
        match type_ {
            ZType::SoftwareBreakpoint | ZType::HardwareBreakpoint => {
                let (rest, (cond_list, cmd_list)) = parse_cond_and_command_list(rest).unwrap();
                let c = (match type_ {
                    ZType::SoftwareBreakpoint => Command::InsertSoftwareBreakpoint,
                    ZType::HardwareBreakpoint => Command::InsertHardwareBreakpoint,
                    _ => panic!("cannot get here"),
                })(Breakpoint::new(addr, kind, cond_list, cmd_list));
                Done(rest, c)
            }
            ZType::WriteWatchpoint => Done(rest, Command::InsertWriteWatchpoint(Watchpoint::new(addr, kind))),
            ZType::ReadWatchpoint => Done(rest, Command::InsertReadWatchpoint(Watchpoint::new(addr, kind))),
            ZType::AccessWatchpoint => Done(rest, Command::InsertAccessWatchpoint(Watchpoint::new(addr, kind))),
        }
    }
    fn remove_command<'a>(type_: ZType, addr: u64, kind: u64) -> Command<'a> {
        match type_ {
            ZType::SoftwareBreakpoint => Command::RemoveSoftwareBreakpoint(Breakpoint::new(addr, kind, None, None)),
            ZType::HardwareBreakpoint => Command::RemoveHardwareBreakpoint(Breakpoint::new(addr, kind, None, None)),
            ZType::WriteWatchpoint => Command::RemoveWriteWatchpoint(Watchpoint::new(addr, kind)),
            ZType::ReadWatchpoint => Command::RemoveReadWatchpoint(Watchpoint::new(addr, kind)),
            ZType::AccessWatchpoint => Command::RemoveAccessWatchpoint(Watchpoint::new(addr, kind)),
        }
    }
}
#[cfg(feature = "all_stop")]
fn all_stop_command(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    alt!(i,
         tag!("c") => { |_| Command::Continue }
         | tag!("s") => { |_| Command::Step }
    )
}
#[cfg(not(feature = "all_stop"))]
fn all_stop_command(_i: &[u8]) -> IResult<&[u8], Command<'_>> {
    IResult::Error(error_position!(ErrorKind::Alt, _i))
}
fn command(i: &[u8]) -> IResult<&[u8], Command<'_>> {
    alt!(i,
         tag!("!") => { |_|   Command::EnableExtendedMode }
         | tag!("?") => { |_| Command::TargetHaltReason }
         | parse_d_packet => { |pid| Command::Detach(pid) }
         | tag!("g") => { |_| Command::ReadGeneralRegisters }
         | write_general_registers => { |bytes| Command::WriteGeneralRegisters(bytes) }
         | parse_h_packet => { |(f, thread_id)| Command::SetCurrentThread(f, thread_id) }
         | tag!("k") => { |_| Command::Kill(None) }
         | read_memory => { |(addr, length)| Command::ReadMemory(MemoryRegion::new(addr, length)) }
         | write_memory => { |(addr, length, bytes)| Command::WriteMemory(MemoryRegion::new(addr, length), bytes) }
         | read_register => { |regno| Command::ReadRegister(regno) }
         | write_register => { |(regno, bytes)| Command::WriteRegister(regno, bytes) }
         | query => { |q| Command::Query(q) }
         | tag!("r") => { |_| Command::Reset }
         | preceded!(tag!("R"), take!(2)) => { |_| Command::Reset }
         | parse_ping_thread => { |thread_id| Command::PingThread(thread_id) }
         | v_command => { |command| command }
         | write_memory_binary => { |(addr, length, bytes)| Command::WriteMemory(MemoryRegion::new(addr, length), bytes) }
         | parse_z_packet => { |command| command }
         | all_stop_command => { |command| command }
    )
}
pub enum Error {
    Error(u8),
    Unimplemented,
}
#[derive(Clone, Copy, Debug)]
pub enum ProcessType {
    Attached,
    Created,
}
#[derive(Clone, Copy, Debug)]
pub enum StopReason {
    Signal(u8),
    Exited(u64, u8),
    ExitedWithSignal(u64, u8),
    ThreadExited(ThreadId, u64),
    NoMoreThreads,
    }
#[derive(Clone, Debug)]
pub enum SymbolLookupResponse {
    Ok,
    Symbol(String),
}
pub trait Handler {
    fn query_supported_features(&self) -> Vec<String> {
        vec![]
    }
    fn attached(&self, _pid: Option<u64>) -> Result<ProcessType, Error>;
    fn detach(&self, _pid: Option<u64>) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn kill(&self, _pid: Option<u64>) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn ping_thread(&self, _id: ThreadId) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn read_memory(&self, _region: MemoryRegion) -> Result<Vec<u8>, Error> {
        Err(Error::Unimplemented)
    }
    fn write_memory(&self, _address: u64, _bytes: &[u8]) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn read_register(&self, _register: u64) -> Result<Vec<u8>, Error> {
        Err(Error::Unimplemented)
    }
    fn write_register(&self, _register: u64, _contents: &[u8]) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn read_general_registers(&self) -> Result<Vec<u8>, Error> {
        Err(Error::Unimplemented)
    }
    fn write_general_registers(&self, _contents: &[u8]) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn read_bytes(
        &self,
        _object: String,
        _annex: String,
        _offset: u64,
        _length: u64,
    ) -> Result<(Vec<u8>, bool), Error> {
        Err(Error::Unimplemented)
    }
    fn current_thread(&self) -> Result<Option<ThreadId>, Error> {
        Ok(None)
    }
    fn set_current_thread(&self, _for: SetThreadFor, _id: ThreadId) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn search_memory(&self, _address: u64, _length: u64, _bytes: &[u8]) -> Result<Option<u64>, Error> {
        Err(Error::Unimplemented)
    }
    fn halt_reason(&self) -> Result<StopReason, Error>;
    fn invoke(&self, _: &[u8]) -> Result<String, Error> {
        Err(Error::Unimplemented)
    }
    fn set_address_randomization(&self, _enable: bool) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn catch_syscalls(&self, _syscalls: Option<Vec<u64>>) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn set_pass_signals(&self, _signals: Vec<u64>) -> Result<(), Error> {
        Ok(())
    }
    fn set_program_signals(&self, _signals: Vec<u64>) -> Result<(), Error> {
        Ok(())
    }
    fn thread_info(&self, _thread: ThreadId) -> Result<String, Error> {
        Err(Error::Unimplemented)
    }
    fn thread_list(&self, _reset: bool) -> Result<Vec<ThreadId>, Error> {
        Err(Error::Unimplemented)
    }
    fn insert_software_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn insert_hardware_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn insert_write_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn insert_read_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn insert_access_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn remove_software_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn remove_hardware_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn remove_write_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn remove_read_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn remove_access_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
        Err(Error::Unimplemented)
    }
    fn query_supported_vcont(&self) -> Result<Cow<'static, [VContFeature]>, Error> {
        Err(Error::Unimplemented)
    }
    fn vcont(&self, _request: Vec<(VCont, Option<ThreadId>)>) -> Result<StopReason, Error> {
        Err(Error::Unimplemented)
    }
    fn fs(&self) -> Result<&dyn FileSystem, ()> {
        Err(())
    }
    #[cfg(feature = "all_stop")]
    fn process_continue(&self) -> Result<StopReason, Error> {
        Err(Error::Unimplemented)
    }
    #[cfg(feature = "all_stop")]
    fn process_step(&self) -> Result<StopReason, Error> {
        Err(Error::Unimplemented)
    }
    fn process_symbol(&self, _sym_value: &str, _sym_name: &str) -> Result<SymbolLookupResponse, Error> {
        Err(Error::Unimplemented)
    }
    #[cfg(feature = "lldb")]
    fn register_info(&self, _reg: u64) -> Result<String, Error> {
        Err(Error::Unimplemented)
    }
    #[cfg(feature = "lldb")]
    fn process_info(&self) -> Result<String, Error> {
        Err(Error::Unimplemented)
    }
}
fn compute_checksum_incremental(bytes: &[u8], init: u8) -> u8 {
    bytes.iter().fold(init, |sum, &b| sum.wrapping_add(b))
}
#[derive(Debug)]
enum HostIOResult {
    Ok,               Error(HostErrno), Integer(u64),     Data(Vec<u8>),    Stat(HostStat),   }
#[derive(Debug)]
enum Response<'a> {
    Empty,
    Ok,
    Error(u8),
    String(Cow<'a, str>),
    Output(String),
    Bytes(Vec<u8>),
    BytesOrEof(Vec<u8>, bool),
    CurrentThread(Option<ThreadId>),
    ProcessType(ProcessType),
    Stopped(StopReason),
    SearchResult(Option<u64>),
    VContFeatures(Cow<'static, [VContFeature]>),
    ThreadList(Vec<ThreadId>),
    HostIOResult(HostIOResult),
    SymbolName(String),
}
impl<'a, T> From<Result<T, Error>> for Response<'a>
where
    Response<'a>: From<T>,
{
    fn from(result: Result<T, Error>) -> Self {
        match result {
            Result::Ok(val) => val.into(),
            Result::Err(Error::Error(val)) => Response::Error(val),
            Result::Err(Error::Unimplemented) => Response::Empty,
        }
    }
}
impl<'a, T> From<Result<T, HostErrno>> for HostIOResult
where
    HostIOResult: From<T>,
{
    fn from(result: Result<T, HostErrno>) -> Self {
        match result {
            Result::Ok(val) => val.into(),
            Result::Err(errno) => HostIOResult::Error(errno),
        }
    }
}
impl From<()> for HostIOResult {
    fn from(_: ()) -> Self {
        HostIOResult::Ok
    }
}
impl From<u64> for HostIOResult {
    fn from(i: u64) -> Self {
        HostIOResult::Integer(i)
    }
}
impl From<Vec<u8>> for HostIOResult {
    fn from(data: Vec<u8>) -> Self {
        HostIOResult::Data(data)
    }
}
impl From<HostStat> for HostIOResult {
    fn from(stat: HostStat) -> Self {
        HostIOResult::Stat(stat)
    }
}
impl<'a, T> From<IOResult<T>> for Response<'a>
where
    HostIOResult: From<T>,
{
    fn from(result: IOResult<T>) -> Self {
        match result {
            Result::Ok(val) => Response::HostIOResult(val.into()),
            Result::Err(_) => Response::Empty,
        }
    }
}
impl<'a> From<()> for Response<'a> {
    fn from(_: ()) -> Self {
        Response::Ok
    }
}
impl<'a> From<Vec<u8>> for Response<'a> {
    fn from(response: Vec<u8>) -> Self {
        Response::Bytes(response)
    }
}
impl<'a> From<(Vec<u8>, bool)> for Response<'a> {
    fn from((data, eof): (Vec<u8>, bool)) -> Self {
        Response::BytesOrEof(data, eof)
    }
}
impl<'a> From<Option<ThreadId>> for Response<'a> {
    fn from(response: Option<ThreadId>) -> Self {
        Response::CurrentThread(response)
    }
}
impl<'a> From<Option<u64>> for Response<'a> {
    fn from(response: Option<u64>) -> Self {
        Response::SearchResult(response)
    }
}
impl<'a> From<ProcessType> for Response<'a> {
    fn from(process_type: ProcessType) -> Self {
        Response::ProcessType(process_type)
    }
}
impl<'a> From<StopReason> for Response<'a> {
    fn from(reason: StopReason) -> Self {
        Response::Stopped(reason)
    }
}
impl<'a> From<String> for Response<'a> {
    fn from(reason: String) -> Self {
        Response::String(Cow::Owned(reason) as Cow<'_, str>)
    }
}
impl<'a> From<Cow<'static, [VContFeature]>> for Response<'a> {
    fn from(features: Cow<'static, [VContFeature]>) -> Self {
        Response::VContFeatures(features)
    }
}
impl<'a> From<Vec<ThreadId>> for Response<'a> {
    fn from(threads: Vec<ThreadId>) -> Self {
        Response::ThreadList(threads)
    }
}
impl<'a> From<SymbolLookupResponse> for Response<'a> {
    fn from(response: SymbolLookupResponse) -> Self {
        match response {
            SymbolLookupResponse::Ok => Response::Ok,
            SymbolLookupResponse::Symbol(n) => Response::SymbolName(n),
        }
    }
}
impl<'a> From<HostIOResult> for Response<'a> {
    fn from(result: HostIOResult) -> Self {
        Response::HostIOResult(result)
    }
}
struct PacketWriter<'a, W>
where
    W: Write,
{
    writer: &'a mut W,
    checksum: u8,
}
impl<'a, W> PacketWriter<'a, W>
where
    W: Write,
{
    fn new(writer: &'a mut W) -> PacketWriter<'a, W> {
        PacketWriter { writer, checksum: 0 }
    }
    fn finish(&mut self) -> io::Result<()> {
        write!(self.writer, "#{:02x}", self.checksum)?;
        self.writer.flush()?;
        self.checksum = 0;
        Ok(())
    }
}
impl<'a, W> Write for PacketWriter<'a, W>
where
    W: Write,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let count = self.writer.write(buf)?;
        self.checksum = compute_checksum_incremental(&buf[0..count], self.checksum);
        Ok(count)
    }
    fn flush(&mut self) -> io::Result<()> {
        self.writer.flush()
    }
}
fn write_thread_id<W>(writer: &mut W, thread_id: ThreadId) -> io::Result<()>
where
    W: Write,
{
    write!(writer, "p")?;
    match thread_id.pid {
        Id::All => write!(writer, "-1"),
        Id::Any => write!(writer, "0"),
        Id::Id(num) => write!(writer, "{:x}", num),
    }?;
    write!(writer, ".")?;
    match thread_id.tid {
        Id::All => write!(writer, "-1"),
        Id::Any => write!(writer, "0"),
        Id::Id(num) => write!(writer, "{:x}", num),
    }
}
impl From<HostStat> for Vec<u8> {
    fn from(stat: HostStat) -> Self {
        let mut v = vec![];
        write_stat(&mut v, stat).unwrap();
        v
    }
}
fn write_hex_data<W>(writer: &mut W, data: &[u8]) -> io::Result<()>
where
    W: Write,
{
    for b in data {
        write!(writer, "{:02x}", b)?;
    }
    Ok(())
}
fn write_binary_data<W>(writer: &mut W, data: &[u8]) -> io::Result<()>
where
    W: Write,
{
    let mut remaining = data;
    loop {
        let unescaped = min(
            memchr::memchr3(b'#', b'$', b'}', remaining).unwrap_or(remaining.len()),
            memchr::memchr(b'*', remaining).unwrap_or(remaining.len()),
        );
        writer.write_all(&remaining[..unescaped])?;
        remaining = &remaining[unescaped..];
        match remaining.first() {
            Some(byte) => writer.write_all(&[b'}', byte ^ 0x20])?,
            None => break,
        }
        remaining = &remaining[1..];
    }
    Ok(())
}
fn write_response<W>(response: Response<'_>, writer: &mut W) -> io::Result<()>
where
    W: Write,
{
    trace!("Response: {:?}", response);
    write!(writer, "$")?;
    let mut writer = PacketWriter::new(writer);
    match response {
        Response::Ok => {
            write!(writer, "OK")?;
        }
        Response::Empty => {}
        Response::Error(val) => {
            write!(writer, "E{:02x}", val)?;
        }
        Response::String(s) => {
            write!(writer, "{}", s)?;
        }
        Response::Output(s) => {
            write!(writer, "O")?;
            for byte in s.as_bytes() {
                write!(writer, "{:02x}", byte)?;
            }
        }
        Response::Bytes(bytes) => {
            for byte in bytes {
                write!(writer, "{:02x}", byte)?;
            }
        }
        Response::BytesOrEof(bytes, eof) => {
            write!(writer, "{}", if eof { "l" } else { "m" })?;
            write_binary_data(&mut writer, &bytes)?;
        }
        Response::CurrentThread(tid) => {
            match tid {
                None => write!(writer, "OK")?,
                Some(thread_id) => {
                    write!(writer, "QC")?;
                    write_thread_id(&mut writer, thread_id)?;
                }
            };
        }
        Response::ProcessType(process_type) => {
            match process_type {
                ProcessType::Attached => write!(writer, "1")?,
                ProcessType::Created => write!(writer, "0")?,
            };
        }
        Response::SearchResult(maybe_addr) => match maybe_addr {
            Some(addr) => write!(writer, "1,{:x}", addr)?,
            None => write!(writer, "0")?,
        },
        Response::Stopped(stop_reason) => {
            match stop_reason {
                StopReason::Signal(signo) => write!(writer, "S{:02x}", signo)?,
                StopReason::Exited(pid, status) => {
                    write!(writer, "W{:02x};process:{:x}", status, pid)?;
                }
                StopReason::ExitedWithSignal(pid, status) => {
                    write!(writer, "X{:x};process:{:x}", status, pid)?;
                }
                StopReason::ThreadExited(thread_id, status) => {
                    write!(writer, "w{:x};", status)?;
                    write_thread_id(&mut writer, thread_id)?;
                }
                StopReason::NoMoreThreads => write!(writer, "N")?,
            }
        }
        Response::VContFeatures(features) => {
            write!(writer, "vCont")?;
            for &feature in &*features {
                write!(writer, ";{}", feature as u8 as char)?;
            }
        }
        Response::ThreadList(threads) => {
            if threads.is_empty() {
                write!(writer, "l")?;
            } else {
                write!(writer, "m")?;
                for (i, &id) in threads.iter().enumerate() {
                    if i != 0 {
                        write!(writer, ",")?;
                    }
                    write_thread_id(&mut writer, id)?;
                }
            }
        }
        Response::SymbolName(name) => {
            write!(writer, "qSymbol:")?;
            write_hex_data(&mut writer, name.as_bytes())?;
        }
        Response::HostIOResult(result) => match result {
            HostIOResult::Ok => write!(writer, "F0")?,
            HostIOResult::Error(errno) => {
                write!(writer, "F-1,{:x}", errno as u32)?;
            }
            HostIOResult::Integer(i) => write!(writer, "F{:x}", i)?,
            HostIOResult::Data(v) => {
                write!(writer, "F{:x};", v.len())?;
                write_binary_data(&mut writer, &v)?;
            }
            HostIOResult::Stat(stat) => {
                let data = <HostStat as Into<Vec<_>>>::into(stat);
                write!(writer, "F{:x};", data.len())?;
                write_binary_data(&mut writer, &data[..])?
            }
        },
    }
    writer.finish()
}
fn handle_supported_features<'a, H>(handler: &H, _features: &[GDBFeatureSupported<'a>]) -> Response<'static>
where
    H: Handler,
{
    let mut features = vec![
        format!("PacketSize={:x}", MAX_PACKET_SIZE),
        "QStartNoAckMode+".to_string(),
        "multiprocess+".to_string(),
        "QDisableRandomization+".to_string(),
        "QCatchSyscalls+".to_string(),
        "QPassSignals+".to_string(),
        "QProgramSignals+".to_string(),
    ];
    let mut new_features = handler.query_supported_features();
    features.append(&mut new_features);
    Response::String(Cow::Owned(features.join(";")) as Cow<'_, str>)
}
fn handle_packet<H, W>(data: &[u8], handler: &H, writer: &mut W) -> io::Result<bool>
where
    H: Handler,
    W: Write,
{
    debug!("Command: {}", String::from_utf8_lossy(data));
    let mut no_ack_mode = false;
    let response = if let Done(_, command) = command(data) {
        match command {
            Command::EnableExtendedMode => Response::Ok,
            Command::TargetHaltReason => handler.halt_reason().into(),
            Command::ReadGeneralRegisters => handler.read_general_registers().into(),
            Command::WriteGeneralRegisters(bytes) => handler.write_general_registers(&bytes[..]).into(),
            Command::Kill(None) => {
                drop(handler.kill(None));
                Response::Empty
            }
            Command::Kill(pid) => handler.kill(pid).into(),
            Command::Reset => Response::Empty,
            Command::ReadRegister(regno) => handler.read_register(regno).into(),
            Command::WriteRegister(regno, bytes) => handler.write_register(regno, &bytes[..]).into(),
            Command::ReadMemory(region) => handler.read_memory(region).into(),
            Command::WriteMemory(region, bytes) => {
                if region.length as usize != bytes.len() {
                    Response::Error(1)
                } else {
                    handler.write_memory(region.address, &bytes[..]).into()
                }
            }
            Command::SetCurrentThread(f, thread_id) => handler.set_current_thread(f, thread_id).into(),
            Command::Detach(pid) => handler.detach(pid).into(),
            #[cfg(feature = "all_stop")]
            Command::Continue => handler.process_continue().into(),
            #[cfg(feature = "all_stop")]
            Command::Step => handler.process_step().into(),
            Command::Query(Query::Attached(pid)) => handler.attached(pid).into(),
            Command::Query(Query::CurrentThread) => handler.current_thread().into(),
            Command::Query(Query::Invoke(cmd)) => match handler.invoke(&cmd[..]) {
                Result::Ok(val) => {
                    if val.is_empty() {
                        Response::Ok
                    } else {
                        Response::Output(val)
                    }
                }
                Result::Err(Error::Error(val)) => Response::Error(val),
                Result::Err(Error::Unimplemented) => Response::Empty,
            },
            Command::Query(Query::SearchMemory { address, length, bytes }) => {
                handler.search_memory(address, length, &bytes[..]).into()
            }
            Command::Query(Query::SupportedFeatures(features)) => handle_supported_features(handler, &features),
            Command::Query(Query::StartNoAckMode) => {
                no_ack_mode = true;
                Response::Ok
            }
            Command::Query(Query::AddressRandomization(randomize)) => {
                handler.set_address_randomization(randomize).into()
            }
            Command::Query(Query::CatchSyscalls(calls)) => handler.catch_syscalls(calls).into(),
            Command::Query(Query::PassSignals(signals)) => handler.set_pass_signals(signals).into(),
            Command::Query(Query::ProgramSignals(signals)) => handler.set_program_signals(signals).into(),
            Command::Query(Query::ThreadInfo(thread_info)) => handler.thread_info(thread_info).into(),
            Command::Query(Query::ThreadList(reset)) => handler.thread_list(reset).into(),
            Command::Query(Query::ReadBytes {
                object,
                annex,
                offset,
                length,
            }) => handler.read_bytes(object, annex, offset, length).into(),
            #[cfg(feature = "lldb")]
            Command::Query(Query::RegisterInfo(reg)) => handler.register_info(reg).into(),
            #[cfg(feature = "lldb")]
            Command::Query(Query::ProcessInfo) => handler.process_info().into(),
            Command::Query(Query::Symbol(sym_value, sym_name)) => handler.process_symbol(&sym_value, &sym_name).into(),
            Command::PingThread(thread_id) => handler.ping_thread(thread_id).into(),
            Command::CtrlC => Response::Empty,
            Command::UnknownVCommand => Response::Empty,
            Command::InsertSoftwareBreakpoint(bp) => handler.insert_software_breakpoint(bp).into(),
            Command::InsertHardwareBreakpoint(bp) => handler.insert_hardware_breakpoint(bp).into(),
            Command::InsertWriteWatchpoint(wp) => handler.insert_write_watchpoint(wp).into(),
            Command::InsertReadWatchpoint(wp) => handler.insert_read_watchpoint(wp).into(),
            Command::InsertAccessWatchpoint(wp) => handler.insert_access_watchpoint(wp).into(),
            Command::RemoveSoftwareBreakpoint(bp) => handler.remove_software_breakpoint(bp).into(),
            Command::RemoveHardwareBreakpoint(bp) => handler.remove_hardware_breakpoint(bp).into(),
            Command::RemoveWriteWatchpoint(wp) => handler.remove_write_watchpoint(wp).into(),
            Command::RemoveReadWatchpoint(wp) => handler.remove_read_watchpoint(wp).into(),
            Command::RemoveAccessWatchpoint(wp) => handler.remove_access_watchpoint(wp).into(),
            Command::VContSupported => handler.query_supported_vcont().into(),
            Command::VCont(list) => handler.vcont(list).into(),
            Command::HostOpen(filename, mode, flags) => handler
                .fs()
                .and_then(|fs| {
                    fs.host_open(
                        filename,
                        HostOpenFlags::from_bits_truncate(mode as u32),
                        HostMode::from_bits_truncate(flags as u32),
                    )
                })
                .into(),
            Command::HostClose(fd) => handler.fs().and_then(|fs| fs.host_close(fd)).into(),
            Command::HostPRead(fd, count, offset) => {
                handler.fs().and_then(|fs| fs.host_pread(fd, count, offset)).into()
            }
            Command::HostPWrite(fd, offset, data) => {
                handler.fs().and_then(|fs| fs.host_pwrite(fd, offset, data)).into()
            }
            Command::HostFStat(fd) => handler.fs().and_then(|fs| fs.host_fstat(fd)).into(),
            Command::HostUnlink(filename) => handler.fs().and_then(|fs| fs.host_unlink(filename)).into(),
            Command::HostReadlink(filename) => handler.fs().and_then(|fs| fs.host_readlink(filename)).into(),
            Command::HostSetFS(pid) => handler.fs().and_then(|fs| fs.host_setfs(pid)).into(),
        }
    } else {
        Response::Empty
    };
    write_response(response, writer)?;
    Ok(no_ack_mode)
}
fn offset(from: &[u8], to: &[u8]) -> usize {
    let fst = from.as_ptr();
    let snd = to.as_ptr();
    snd as usize - fst as usize
}
fn run_parser(buf: &[u8]) -> Option<(usize, Packet)> {
    if let Done(rest, packet) = packet_or_response(buf) {
        Some((offset(buf, rest), packet))
    } else {
        None
    }
}
pub fn process_packets_from<R, W, H>(mut reader: R, mut writer: W, handler: H)
where
    R: Read,
    W: Write,
    H: Handler,
{
    let mut buf = vec![0; MAX_PACKET_SIZE];
    let mut endbuf = 0;
    let mut ack_mode = true;
    loop {
        match reader.read(&mut buf[endbuf..]) {
            Ok(n) if n > 0 => endbuf += n,
            _ => break,
        }
        let mut parsed = 0;
        while let Some((len, packet)) = run_parser(&buf[parsed..endbuf]) {
            if let Packet::Data(ref data, ref _checksum) = packet {
                if ack_mode && writer.write_all(&b"+"[..]).is_err() {
                    return;
                }
                let no_ack_mode = handle_packet(&data, &handler, &mut writer).unwrap_or(false);
                if no_ack_mode {
                    ack_mode = false;
                }
            }
            parsed += len;
        }
        buf.copy_within(parsed..endbuf, 0);
        endbuf -= parsed;
    }
}
#[test]
fn test_compute_checksum() {
    assert_eq!(compute_checksum_incremental(&b""[..], 0), 0);
    assert_eq!(
        compute_checksum_incremental(&b"qSupported:multiprocess+;xmlRegisters=i386;qRelocInsn+"[..], 0),
        0xb5
    );
}
#[test]
fn test_checksum() {
    assert_eq!(checksum(&b"00"[..]), Done(&b""[..], 0));
    assert_eq!(checksum(&b"a1"[..]), Done(&b""[..], 0xa1));
    assert_eq!(checksum(&b"1d"[..]), Done(&b""[..], 0x1d));
    assert_eq!(checksum(&b"ff"[..]), Done(&b""[..], 0xff));
}
#[test]
fn test_packet() {
    use nom::Needed;
    assert_eq!(packet(&b"$#00"[..]), Done(&b""[..], (b""[..].to_vec(), 0)));
    assert_eq!(packet(&b"$xyz#00"[..]), Done(&b""[..], (b"xyz"[..].to_vec(), 0)));
    assert_eq!(packet(&b"$a#a1"[..]), Done(&b""[..], (b"a"[..].to_vec(), 0xa1)));
    assert_eq!(
        packet(&b"$foo#ffxyz"[..]),
        Done(&b"xyz"[..], (b"foo"[..].to_vec(), 0xff))
    );
    assert_eq!(
        packet(&b"$qSupported:multiprocess+;xmlRegisters=i386;qRelocInsn+#b5"[..]),
        Done(
            &b""[..],
            (
                b"qSupported:multiprocess+;xmlRegisters=i386;qRelocInsn+"[..].to_vec(),
                0xb5
            )
        )
    );
    assert_eq!(packet(&b"$"[..]), Incomplete(Needed::Size(2)));
    assert_eq!(packet(&b"$#"[..]), Incomplete(Needed::Size(4)));
    assert_eq!(packet(&b"$xyz"[..]), Incomplete(Needed::Size(5)));
    assert_eq!(packet(&b"$xyz#"[..]), Incomplete(Needed::Size(7)));
    assert_eq!(packet(&b"$xyz#a"[..]), Incomplete(Needed::Size(7)));
}
#[test]
fn test_packet_or_response() {
    assert_eq!(
        packet_or_response(&b"$#00"[..]),
        Done(&b""[..], Packet::Data(b""[..].to_vec(), 0))
    );
    assert_eq!(packet_or_response(&b"+"[..]), Done(&b""[..], Packet::Ack));
    assert_eq!(packet_or_response(&b"-"[..]), Done(&b""[..], Packet::Nack));
}
#[test]
fn test_gdbfeaturesupported() {
    assert_eq!(
        gdbfeaturesupported(&b"multiprocess+"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::Yes(GDBFeature::multiprocess), FeatureSupported::Yes)
        )
    );
    assert_eq!(
        gdbfeaturesupported(&b"xmlRegisters=i386"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::Yes(GDBFeature::xmlRegisters), FeatureSupported::Value("i386"))
        )
    );
    assert_eq!(
        gdbfeaturesupported(&b"qRelocInsn-"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::Yes(GDBFeature::qRelocInsn), FeatureSupported::No)
        )
    );
    assert_eq!(
        gdbfeaturesupported(&b"vfork-events+"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::Yes(GDBFeature::vfork_events), FeatureSupported::Yes)
        )
    );
    assert_eq!(
        gdbfeaturesupported(&b"vfork-events-"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::Yes(GDBFeature::vfork_events), FeatureSupported::No)
        )
    );
    assert_eq!(
        gdbfeaturesupported(&b"unknown-feature+"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::No("unknown-feature"), FeatureSupported::Yes)
        )
    );
    assert_eq!(
        gdbfeaturesupported(&b"unknown-feature-"[..]),
        Done(
            &b""[..],
            GDBFeatureSupported(Known::No("unknown-feature"), FeatureSupported::No)
        )
    );
}
#[test]
fn test_gdbfeature() {
    assert_eq!(
        gdbfeature(&b"multiprocess"[..]),
        Done(&b""[..], Known::Yes(GDBFeature::multiprocess))
    );
    assert_eq!(
        gdbfeature(&b"fork-events"[..]),
        Done(&b""[..], Known::Yes(GDBFeature::fork_events))
    );
    assert_eq!(
        gdbfeature(&b"some-unknown-feature"[..]),
        Done(&b""[..], Known::No("some-unknown-feature"))
    );
}
#[test]
fn test_query() {
    let b = concat!(
        "qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;",
        "vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;",
        "xmlRegisters=i386"
    );
    assert_eq!(
        query(b.as_bytes()),
        Done(
            &b""[..],
            Query::SupportedFeatures(vec![
                GDBFeatureSupported(Known::Yes(GDBFeature::multiprocess), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::swbreak), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::hwbreak), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::qRelocInsn), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::fork_events), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::vfork_events), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::exec_events), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::vContSupported), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::QThreadEvents), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::no_resumed), FeatureSupported::Yes),
                GDBFeatureSupported(Known::Yes(GDBFeature::xmlRegisters), FeatureSupported::Value("i386")),
            ])
        )
    );
}
#[test]
fn test_hex_value() {
    assert_eq!(hex_value(&b""[..]), Incomplete(Needed::Size(1)));
    assert_eq!(hex_value(&b","[..]), Error(nom::ErrorKind::TakeWhile1));
    assert_eq!(hex_value(&b"a"[..]), Done(&b""[..], 0xa));
    assert_eq!(hex_value(&b"10,"[..]), Done(&b","[..], 0x10));
    assert_eq!(hex_value(&b"ff"[..]), Done(&b""[..], 0xff));
}
#[test]
fn test_parse_thread_id_element() {
    assert_eq!(parse_thread_id_element(&b"0"[..]), Done(&b""[..], Id::Any));
    assert_eq!(parse_thread_id_element(&b"-1"[..]), Done(&b""[..], Id::All));
    assert_eq!(parse_thread_id_element(&b"23"[..]), Done(&b""[..], Id::Id(0x23)));
}
#[test]
fn test_parse_thread_id() {
    assert_eq!(
        parse_thread_id(&b"0"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::Any,
                tid: Id::Any
            }
        )
    );
    assert_eq!(
        parse_thread_id(&b"-1"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::All,
                tid: Id::Any
            }
        )
    );
    assert_eq!(
        parse_thread_id(&b"23"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::Id(0x23),
                tid: Id::Any
            }
        )
    );
    assert_eq!(
        parse_thread_id(&b"p23"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::Id(0x23),
                tid: Id::All
            }
        )
    );
    assert_eq!(
        parse_thread_id(&b"p0.0"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::Any,
                tid: Id::Any
            }
        )
    );
    assert_eq!(
        parse_thread_id(&b"p-1.23"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::All,
                tid: Id::Id(0x23)
            }
        )
    );
    assert_eq!(
        parse_thread_id(&b"pff.23"[..]),
        Done(
            &b""[..],
            ThreadId {
                pid: Id::Id(0xff),
                tid: Id::Id(0x23)
            }
        )
    );
}
#[test]
fn test_parse_v_commands() {
    assert_eq!(v_command(&b"vKill;33"[..]), Done(&b""[..], Command::Kill(Some(0x33))));
    assert_eq!(v_command(&b"vCtrlC"[..]), Done(&b""[..], Command::CtrlC));
    assert_eq!(
        v_command(&b"vMustReplyEmpty"[..]),
        Done(&b""[..], Command::UnknownVCommand)
    );
    assert_eq!(v_command(&b"vCont?"[..]), Done(&b""[..], Command::VContSupported));
    assert_eq!(v_command(&b"vCont"[..]), Done(&b""[..], Command::VCont(Vec::new())));
    assert_eq!(
        v_command(&b"vCont;c"[..]),
        Done(&b""[..], Command::VCont(vec![(VCont::Continue, None)]))
    );
    assert_eq!(
        v_command(&b"vCont;r1,2:p34.56;SAD:-1;c"[..]),
        Done(
            &b""[..],
            Command::VCont(vec![
                (
                    VCont::RangeStep(1..2),
                    Some(ThreadId {
                        pid: Id::Id(0x34),
                        tid: Id::Id(0x56)
                    })
                ),
                (
                    VCont::StepWithSignal(0xAD),
                    Some(ThreadId {
                        pid: Id::All,
                        tid: Id::Any
                    })
                ),
                (VCont::Continue, None)
            ])
        )
    );
    assert_eq!(
        v_command(&b"vFile:open:2f766d6c696e757a,0,0"[..]),
        Done(
            &b""[..],
            Command::HostOpen(vec!(47, 118, 109, 108, 105, 110, 117, 122), 0, 0)
        )
    );
    assert_eq!(
        v_command(&b"vFile:close:85"[..]),
        Done(&b""[..], Command::HostClose(0x85))
    );
    assert_eq!(
        v_command(&b"vFile:pread:5,96,327"[..]),
        Done(&b""[..], Command::HostPRead(5, 0x96, 0x327))
    );
    assert_eq!(
        v_command(&b"vFile:pwrite:6,83,\x00"[..]),
        Done(&b""[..], Command::HostPWrite(6, 0x83, vec![0]))
    );
    assert_eq!(
        v_command(&b"vFile:pwrite:6,83,\x7d\x5d"[..]),
        Done(&b""[..], Command::HostPWrite(6, 0x83, vec![0x7d]))
    );
    assert_eq!(
        v_command(&b"vFile:fstat:32"[..]),
        Done(&b""[..], Command::HostFStat(0x32))
    );
    assert_eq!(
        v_command(&b"vFile:unlink:2f766d6c696e757a"[..]),
        Done(
            &b""[..],
            Command::HostUnlink(vec!(47, 118, 109, 108, 105, 110, 117, 122))
        )
    );
    assert_eq!(
        v_command(&b"vFile:readlink:2f766d6c696e757a"[..]),
        Done(
            &b""[..],
            Command::HostReadlink(vec!(47, 118, 109, 108, 105, 110, 117, 122))
        )
    );
    assert_eq!(v_command(&b"vFile:setfs:0"[..]), Done(&b""[..], Command::HostSetFS(0)));
    assert_eq!(
        v_command(&b"vFile:fdopen:0"[..]),
        Done(&b""[..], Command::UnknownVCommand)
    );
}
#[test]
fn test_parse_d_packets() {
    assert_eq!(parse_d_packet(&b"D"[..]), Done(&b""[..], None));
    assert_eq!(parse_d_packet(&b"D;f0"[..]), Done(&b""[..], Some(240)));
}
#[test]
fn test_parse_write_memory() {
    assert_eq!(
        write_memory(&b"Mf0,3:ff0102"[..]),
        Done(&b""[..], (240, 3, vec!(255, 1, 2)))
    );
}
#[test]
fn test_parse_write_memory_binary() {
    assert_eq!(
        write_memory_binary(&b"Xf0,1: "[..]),
        Done(&b""[..], (240, 1, vec!(0x20)))
    );
    assert_eq!(
        write_memory_binary(&b"X90,10:}\x5d"[..]),
        Done(&b""[..], (144, 16, vec!(0x7d)))
    );
    assert_eq!(
        write_memory_binary(&b"X5,100:}\x5d}\x03"[..]),
        Done(&b""[..], (5, 256, vec!(0x7d, 0x23)))
    );
    assert_eq!(
        write_memory_binary(&b"Xff,2:}\x04\x9a"[..]),
        Done(&b""[..], (255, 2, vec!(0x24, 0x9a)))
    );
    assert_eq!(
        write_memory_binary(&b"Xff,2:\xce}\x0a\x9a"[..]),
        Done(&b""[..], (255, 2, vec!(0xce, 0x2a, 0x9a)))
    );
}
#[test]
fn test_parse_qrcmd() {
    assert_eq!(
        query(&b"qRcmd,736f6d657468696e67"[..]),
        Done(&b""[..], Query::Invoke(b"something".to_vec()))
    );
}
#[test]
fn test_parse_randomization() {
    assert_eq!(
        query(&b"QDisableRandomization:0"[..]),
        Done(&b""[..], Query::AddressRandomization(true))
    );
    assert_eq!(
        query(&b"QDisableRandomization:1"[..]),
        Done(&b""[..], Query::AddressRandomization(false))
    );
}
#[test]
fn test_parse_syscalls() {
    assert_eq!(
        query(&b"QCatchSyscalls:0"[..]),
        Done(&b""[..], Query::CatchSyscalls(None))
    );
    assert_eq!(
        query(&b"QCatchSyscalls:1"[..]),
        Done(&b""[..], Query::CatchSyscalls(Some(vec!())))
    );
    assert_eq!(
        query(&b"QCatchSyscalls:1;0;1;ff"[..]),
        Done(&b""[..], Query::CatchSyscalls(Some(vec!(0, 1, 255))))
    );
}
#[test]
fn test_parse_signals() {
    assert_eq!(query(&b"QPassSignals:"[..]), Done(&b""[..], Query::PassSignals(vec!())));
    assert_eq!(
        query(&b"QPassSignals:0"[..]),
        Done(&b""[..], Query::PassSignals(vec!(0)))
    );
    assert_eq!(
        query(&b"QPassSignals:1;2;ff"[..]),
        Done(&b""[..], Query::PassSignals(vec!(1, 2, 255)))
    );
    assert_eq!(
        query(&b"QProgramSignals:0"[..]),
        Done(&b""[..], Query::ProgramSignals(vec!(0)))
    );
    assert_eq!(
        query(&b"QProgramSignals:1;2;ff"[..]),
        Done(&b""[..], Query::ProgramSignals(vec!(1, 2, 255)))
    );
}
#[test]
fn test_thread_info() {
    assert_eq!(
        query(&b"qThreadExtraInfo,ffff"[..]),
        Done(
            &b""[..],
            Query::ThreadInfo(ThreadId {
                pid: Id::Id(65535),
                tid: Id::Any
            })
        )
    );
}
#[test]
fn test_thread_list() {
    assert_eq!(query(&b"qfThreadInfo"[..]), Done(&b""[..], Query::ThreadList(true)));
    assert_eq!(query(&b"qsThreadInfo"[..]), Done(&b""[..], Query::ThreadList(false)));
}
#[test]
fn test_parse_write_register() {
    assert_eq!(write_register(&b"Pff=1020"[..]), Done(&b""[..], (255, vec!(16, 32))));
}
#[test]
fn test_parse_write_general_registers() {
    assert_eq!(
        write_general_registers(&b"G0001020304"[..]),
        Done(&b""[..], vec!(0, 1, 2, 3, 4))
    );
}
#[test]
fn test_write_response() {
    fn write_one(input: Response<'_>) -> io::Result<String> {
        let mut result = Vec::new();
        write_response(input, &mut result)?;
        Ok(String::from_utf8(result).unwrap())
    }
    assert_eq!(write_one(Response::Empty).unwrap(), "$#00");
    assert_eq!(write_one(Response::Ok).unwrap(), "$OK#9a");
    assert_eq!(write_one(Response::Error(1)).unwrap(), "$E01#a6");
    assert_eq!(
        write_one(Response::CurrentThread(Some(ThreadId {
            pid: Id::Id(255),
            tid: Id::Id(1)
        })))
        .unwrap(),
        "$QCpff.1#2f"
    );
    assert_eq!(
        write_one(Response::ThreadList(vec!(
            ThreadId {
                pid: Id::Id(123),
                tid: Id::Id(123)
            },
            ThreadId {
                pid: Id::Id(456),
                tid: Id::Any
            },
        )))
        .unwrap(),
        "$mp7b.7b,p1c8.0#03"
    );
    assert_eq!(write_one(Response::ThreadList(vec!())).unwrap(), "$l#6c");
    assert_eq!(
        write_one(Response::BytesOrEof(
            Vec::from(&b"{Hello * World} #yee $999.99"[..]),
            false
        ))
        .unwrap(),
        "$m{Hello }\n World}] }\x03yee }\x04999.99#54"
    );
    assert_eq!(
        write_one(Response::BytesOrEof(
            Vec::from(&b"does not need to be escaped"[..]),
            true
        ))
        .unwrap(),
        "$ldoes not need to be escaped#23"
    );
}
#[cfg(test)]
macro_rules! bytecode {
    ($elem:expr; $n:expr) => (Bytecode { bytecode: vec![$elem; $n] });
    ($($x:expr),*) => (Bytecode { bytecode: vec!($($x),*) })
}
#[test]
fn test_breakpoints() {
    assert_eq!(
        parse_z_packet(&b"Z0,1ff,0"[..]),
        Done(
            &b""[..],
            Command::InsertSoftwareBreakpoint(Breakpoint::new(0x1ff, 0, None, None))
        )
    );
    assert_eq!(
        parse_z_packet(&b"z0,1fff,0"[..]),
        Done(
            &b""[..],
            Command::RemoveSoftwareBreakpoint(Breakpoint::new(0x1fff, 0, None, None))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z1,ae,0"[..]),
        Done(
            &b""[..],
            Command::InsertHardwareBreakpoint(Breakpoint::new(0xae, 0, None, None))
        )
    );
    assert_eq!(
        parse_z_packet(&b"z1,aec,0"[..]),
        Done(
            &b""[..],
            Command::RemoveHardwareBreakpoint(Breakpoint::new(0xaec, 0, None, None))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z2,4cc,2"[..]),
        Done(&b""[..], Command::InsertWriteWatchpoint(Watchpoint::new(0x4cc, 2)))
    );
    assert_eq!(
        parse_z_packet(&b"z2,4ccf,4"[..]),
        Done(&b""[..], Command::RemoveWriteWatchpoint(Watchpoint::new(0x4ccf, 4)))
    );
    assert_eq!(
        parse_z_packet(&b"Z3,7777,4"[..]),
        Done(&b""[..], Command::InsertReadWatchpoint(Watchpoint::new(0x7777, 4)))
    );
    assert_eq!(
        parse_z_packet(&b"z3,77778,8"[..]),
        Done(&b""[..], Command::RemoveReadWatchpoint(Watchpoint::new(0x77778, 8)))
    );
    assert_eq!(
        parse_z_packet(&b"Z4,7777,10"[..]),
        Done(&b""[..], Command::InsertAccessWatchpoint(Watchpoint::new(0x7777, 16)))
    );
    assert_eq!(
        parse_z_packet(&b"z4,77778,20"[..]),
        Done(&b""[..], Command::RemoveAccessWatchpoint(Watchpoint::new(0x77778, 32)))
    );
    assert_eq!(
        parse_z_packet(&b"Z0,1ff,2;X1,0"[..]),
        Done(
            &b""[..],
            Command::InsertSoftwareBreakpoint(Breakpoint::new(0x1ff, 2, Some(vec!(bytecode!('0' as u8))), None))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z1,1ff,2;X1,0"[..]),
        Done(
            &b""[..],
            Command::InsertHardwareBreakpoint(Breakpoint::new(0x1ff, 2, Some(vec!(bytecode!('0' as u8))), None))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z0,1ff,2;cmdsX1,z"[..]),
        Done(
            &b""[..],
            Command::InsertSoftwareBreakpoint(Breakpoint::new(0x1ff, 2, None, Some(vec!(bytecode!('z' as u8)))))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z1,1ff,2;cmdsX1,z"[..]),
        Done(
            &b""[..],
            Command::InsertHardwareBreakpoint(Breakpoint::new(0x1ff, 2, None, Some(vec!(bytecode!('z' as u8)))))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z0,1ff,2;X1,0;cmdsX1,a"[..]),
        Done(
            &b""[..],
            Command::InsertSoftwareBreakpoint(Breakpoint::new(
                0x1ff,
                2,
                Some(vec!(bytecode!('0' as u8))),
                Some(vec!(bytecode!('a' as u8)))
            ))
        )
    );
    assert_eq!(
        parse_z_packet(&b"Z1,1ff,2;X1,0;cmdsX1,a"[..]),
        Done(
            &b""[..],
            Command::InsertHardwareBreakpoint(Breakpoint::new(
                0x1ff,
                2,
                Some(vec!(bytecode!('0' as u8))),
                Some(vec!(bytecode!('a' as u8)))
            ))
        )
    );
}
#[test]
fn test_cond_or_command_list() {
    assert_eq!(
        parse_condition_list(&b";X1,a"[..]),
        Done(&b""[..], vec!(bytecode!('a' as u8)))
    );
    assert_eq!(
        parse_condition_list(&b";X2,ab"[..]),
        Done(&b""[..], vec!(bytecode!('a' as u8, 'b' as u8)))
    );
    assert_eq!(
        parse_condition_list(&b";X1,zX1,y"[..]),
        Done(&b""[..], vec!(bytecode!('z' as u8), bytecode!('y' as u8)))
    );
    assert_eq!(
        parse_condition_list(&b";X1,zX10,yyyyyyyyyyyyyyyy"[..]),
        Done(&b""[..], vec!(bytecode!('z' as u8), bytecode!['y' as u8; 16]))
    );
    assert_eq!(
        parse_command_list(&b";cmdsX1,a"[..]),
        Done(&b""[..], vec!(bytecode!('a' as u8)))
    );
    assert_eq!(
        parse_command_list(&b";cmdsX2,ab"[..]),
        Done(&b""[..], vec!(bytecode!('a' as u8, 'b' as u8)))
    );
    assert_eq!(
        parse_command_list(&b";cmdsX1,zX1,y"[..]),
        Done(&b""[..], vec!(bytecode!('z' as u8), bytecode!('y' as u8)))
    );
    assert_eq!(
        parse_command_list(&b";cmdsX1,zX10,yyyyyyyyyyyyyyyy"[..]),
        Done(&b""[..], vec!(bytecode!('z' as u8), bytecode!['y' as u8; 16]))
    );
}
#[test]
fn test_qxfer() {
    assert_eq!(
        query(&b"qXfer:features:read:target.xml:0,1000"[..]),
        Done(
            &b""[..],
            Query::ReadBytes {
                object: String::from("features"),
                annex: String::from("target.xml"),
                offset: 0,
                length: 4096,
            }
        )
    );
}
#[test]
fn test_write_binary_data() {
    check(vec![1, 2, 3], vec![1, 2, 3]);
    check(vec![0x7d, 2, 3], vec![0x7d, 0x5d, 2, 3]);
    check(vec![0x23, 2, 3], vec![0x7d, 0x03, 2, 3]);
    check(vec![0x24, 2, 3], vec![0x7d, 0x04, 2, 3]);
    check(vec![0x2a, 2, 3], vec![0x7d, 0x0a, 2, 3]);
    check(vec![8, 9, 0x7d], vec![8, 9, 0x7d, 0x5d]);
    check(vec![8, 9, 0x23], vec![8, 9, 0x7d, 0x03]);
    check(vec![8, 9, 0x24], vec![8, 9, 0x7d, 0x04]);
    check(vec![8, 9, 0x2a], vec![8, 9, 0x7d, 0x0a]);
    check(vec![8, 9, 0x7d, 5, 6], vec![8, 9, 0x7d, 0x5d, 5, 6]);
    check(vec![8, 9, 0x23, 5, 6], vec![8, 9, 0x7d, 0x03, 5, 6]);
    check(vec![8, 9, 0x24, 5, 6], vec![8, 9, 0x7d, 0x04, 5, 6]);
    check(vec![8, 9, 0x2a, 5, 6], vec![8, 9, 0x7d, 0x0a, 5, 6]);
    fn check(data: Vec<u8>, output: Vec<u8>) {
        let mut v = vec![];
        write_binary_data(&mut v, &data).unwrap();
        assert_eq!(v, output);
    }
}