cbor2 1.1.2

Full-featured CBOR (RFC 8949) for serde: async item I/O, canonical encoding, no_std, Value/RawValue, simple values, tags, COSE keys, well-formedness checks and diagnostics.
Documentation
use alloc::vec::Vec;

use crate::de::Error;

use super::encode::{write_definite_bytes, write_uint};
use super::types::{Atom, Indicator};

pub(super) fn ip_atom(content: &str, tagged: bool, offset: usize) -> Result<Atom, Error> {
    let (addr, prefix) = match content.split_once('/') {
        Some((addr, prefix)) => {
            if prefix.is_empty() || !prefix.bytes().all(|b| b.is_ascii_digit()) {
                return Err(Error::Syntax(offset));
            }
            let prefix = prefix
                .parse::<u8>()
                .map_err(|_| Error::Syntax(offset + addr.len() + 1))?;
            (addr, Some(prefix))
        }
        None => (content, None),
    };

    let parsed = if addr.contains(':') {
        IpAddr::V6(parse_ipv6(addr, offset)?)
    } else {
        IpAddr::V4(parse_ipv4(addr, offset)?)
    };

    let (tag_number, max_prefix, bytes) = match parsed {
        IpAddr::V4(bytes) => (52, 32, bytes.to_vec()),
        IpAddr::V6(bytes) => (54, 128, bytes.to_vec()),
    };

    let raw = if let Some(prefix) = prefix {
        if prefix > max_prefix {
            return Err(Error::semantic(offset, "IP prefix length is out of range"));
        }
        let mut prefix_bytes = mask_prefix(bytes, prefix);
        while prefix_bytes.last() == Some(&0) {
            prefix_bytes.pop();
        }
        let mut array = Vec::new();
        write_uint(&mut array, 4, 2, Indicator::None)
            .map_err(|msg| Error::semantic(offset, msg))?;
        write_uint(&mut array, 0, u64::from(prefix), Indicator::None)
            .map_err(|msg| Error::semantic(offset, msg))?;
        write_definite_bytes(&mut array, &prefix_bytes, Indicator::None)?;
        array
    } else {
        let mut out = Vec::new();
        write_definite_bytes(&mut out, &bytes, Indicator::None)?;
        if !tagged {
            return Ok(Atom::Bytes(bytes));
        }
        out
    };

    if tagged {
        let mut out = Vec::new();
        write_uint(&mut out, 6, tag_number, Indicator::None)
            .map_err(|msg| Error::semantic(offset, msg))?;
        out.extend_from_slice(&raw);
        Ok(Atom::Raw(out))
    } else if prefix.is_some() {
        Ok(Atom::Raw(raw))
    } else {
        unreachable!()
    }
}

enum IpAddr {
    V4([u8; 4]),
    V6([u8; 16]),
}

pub(super) fn parse_ipv4(input: &str, offset: usize) -> Result<[u8; 4], Error> {
    let mut out = [0u8; 4];
    let mut count = 0usize;
    for part in input.split('.') {
        if count == 4 || part.is_empty() || !part.bytes().all(|b| b.is_ascii_digit()) {
            return Err(Error::Syntax(offset));
        }
        let value = part.parse::<u16>().map_err(|_| Error::Syntax(offset))?;
        if value > 255 {
            return Err(Error::Syntax(offset));
        }
        out[count] = value as u8;
        count += 1;
    }
    if count == 4 {
        Ok(out)
    } else {
        Err(Error::Syntax(offset))
    }
}

pub(super) fn parse_ipv6(input: &str, offset: usize) -> Result<[u8; 16], Error> {
    if input.is_empty() {
        return Err(Error::Syntax(offset));
    }
    let (left, right, compressed) = match input.split_once("::") {
        Some((left, right)) => {
            if right.contains("::") {
                return Err(Error::Syntax(offset));
            }
            (left, right, true)
        }
        None => (input, "", false),
    };

    let mut groups = Vec::new();
    parse_ipv6_side(left, !compressed && right.is_empty(), offset, &mut groups)?;
    let left_len = groups.len();
    if compressed {
        let mut right_groups = Vec::new();
        parse_ipv6_side(
            right,
            true,
            offset + input.len() - right.len(),
            &mut right_groups,
        )?;
        if left_len + right_groups.len() > 7 {
            return Err(Error::Syntax(offset));
        }
        let zeroes = 8 - left_len - right_groups.len();
        groups.extend(core::iter::repeat_n(0, zeroes));
        groups.extend(right_groups);
    }

    if groups.len() != 8 {
        return Err(Error::Syntax(offset));
    }

    let mut out = [0u8; 16];
    for (idx, group) in groups.into_iter().enumerate() {
        out[idx * 2..idx * 2 + 2].copy_from_slice(&group.to_be_bytes());
    }
    Ok(out)
}

fn parse_ipv6_side(
    side: &str,
    allow_ipv4_tail: bool,
    offset: usize,
    groups: &mut Vec<u16>,
) -> Result<(), Error> {
    if side.is_empty() {
        return Ok(());
    }
    for (idx, part) in side.split(':').enumerate() {
        if part.is_empty() {
            return Err(Error::Syntax(offset));
        }
        if part.contains('.') {
            if !allow_ipv4_tail || idx + 1 != side.split(':').count() {
                return Err(Error::Syntax(offset));
            }
            let v4 = parse_ipv4(part, offset)?;
            groups.push(u16::from_be_bytes([v4[0], v4[1]]));
            groups.push(u16::from_be_bytes([v4[2], v4[3]]));
            continue;
        }
        if part.len() > 4 || !part.bytes().all(|b| b.is_ascii_hexdigit()) {
            return Err(Error::Syntax(offset));
        }
        groups.push(u16::from_str_radix(part, 16).map_err(|_| Error::Syntax(offset))?);
    }
    Ok(())
}

fn mask_prefix(mut bytes: Vec<u8>, prefix: u8) -> Vec<u8> {
    let full = usize::from(prefix / 8);
    let rem = prefix % 8;
    if rem == 0 {
        bytes.truncate(full);
    } else {
        bytes.truncate(full + 1);
        let mask = 0xff << (8 - rem);
        if let Some(last) = bytes.last_mut() {
            *last &= mask;
        }
    }
    bytes
}