gdbstub 0.7.10

An implementation of the GDB Remote Serial Protocol in Rust
Documentation
use crate::protocol::common::hex::decode_hex;
use core::convert::TryFrom;
use core::convert::TryInto;
use core::num::NonZeroUsize;

/// Tid/Pid Selector.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum IdKind {
    /// All threads (-1)
    All,
    /// Any thread (0)
    Any,
    /// Thread with specific ID (id > 0)
    WithId(NonZeroUsize),
}

/// Unique Thread ID.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct ThreadId {
    /// Process ID (may or may not be present).
    pub pid: Option<IdKind>,
    /// Thread ID.
    pub tid: IdKind,
}

impl TryFrom<&[u8]> for ThreadId {
    type Error = ();

    fn try_from(s: &[u8]) -> Result<Self, ()> {
        match s {
            [b'p', s @ ..] => {
                // p<pid>.<tid>
                let mut s = s.split(|b| *b == b'.');
                let pid: IdKind = s.next().ok_or(())?.try_into()?;
                let tid: IdKind = match s.next() {
                    Some(s) => s.try_into()?,
                    None => IdKind::All, // sending only p<pid> is valid
                };

                Ok(ThreadId {
                    pid: Some(pid),
                    tid,
                })
            }
            _ => {
                // <tid>
                let tid: IdKind = s.try_into()?;

                Ok(ThreadId { pid: None, tid })
            }
        }
    }
}

impl TryFrom<&[u8]> for IdKind {
    type Error = ();

    fn try_from(s: &[u8]) -> Result<Self, ()> {
        Ok(match s {
            b"-1" => IdKind::All,
            b"0" => IdKind::Any,
            id => IdKind::WithId(NonZeroUsize::new(decode_hex(id).map_err(drop)?).ok_or(())?),
        })
    }
}

impl TryFrom<&mut [u8]> for ThreadId {
    type Error = ();

    fn try_from(s: &mut [u8]) -> Result<Self, ()> {
        Self::try_from(s as &[u8])
    }
}

impl TryFrom<&mut [u8]> for IdKind {
    type Error = ();

    fn try_from(s: &mut [u8]) -> Result<Self, ()> {
        Self::try_from(s as &[u8])
    }
}

/// Like [`IdKind`], without the `Any` variant. Typically used when working
/// with vCont (i.e: where the `Any` variant wouldn't be valid).
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum SpecificIdKind {
    /// Thread with specific ID (id > 0)
    WithId(core::num::NonZeroUsize),
    /// All threads (-1)
    All,
}

/// Like [`ThreadId`], without the `Any` variants. Typically used when working
/// with vCont (i.e: where the `Any` variant wouldn't be valid).
#[derive(Debug, Copy, Clone)]
pub struct SpecificThreadId {
    /// Process ID (may or may not be present).
    pub pid: Option<SpecificIdKind>,
    /// Thread ID.
    pub tid: SpecificIdKind,
}

impl TryFrom<IdKind> for SpecificIdKind {
    type Error = ();

    fn try_from(id: IdKind) -> Result<SpecificIdKind, ()> {
        Ok(match id {
            IdKind::All => SpecificIdKind::All,
            IdKind::WithId(id) => SpecificIdKind::WithId(id),
            IdKind::Any => return Err(()),
        })
    }
}

impl TryFrom<ThreadId> for SpecificThreadId {
    type Error = ();

    fn try_from(thread: ThreadId) -> Result<SpecificThreadId, ()> {
        Ok(SpecificThreadId {
            pid: match thread.pid {
                None => None,
                Some(id_kind) => Some(id_kind.try_into()?),
            },
            tid: thread.tid.try_into()?,
        })
    }
}

/// Like [`ThreadId`], without the `Any`, or `All` variants.
#[derive(Debug, Copy, Clone)]
pub struct ConcreteThreadId {
    /// Process ID (may or may not be present).
    pub pid: Option<NonZeroUsize>,
    /// Thread ID.
    pub tid: NonZeroUsize,
}

impl TryFrom<ThreadId> for ConcreteThreadId {
    type Error = ();

    fn try_from(thread: ThreadId) -> Result<ConcreteThreadId, ()> {
        Ok(ConcreteThreadId {
            pid: match thread.pid {
                None => None,
                Some(id_kind) => Some(id_kind.try_into()?),
            },
            tid: thread.tid.try_into()?,
        })
    }
}

impl TryFrom<IdKind> for NonZeroUsize {
    type Error = ();

    fn try_from(value: IdKind) -> Result<NonZeroUsize, ()> {
        match value {
            IdKind::WithId(v) => Ok(v),
            _ => Err(()),
        }
    }
}