bitcoin_slices 0.12.0

Parse Bitcoin objects without allocations
use crate::{
    number::{U16, U32, U64},
    visit::Parse,
    Error,
};

/// The bitcoin compact int encoding, up to 253 it consumes 1 byte, then there are markers for
/// `u16`, `u32` or `u64`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Len {
    pub(crate) consumed: usize,
    pub(crate) n: u64,
}

/// Parse `Len` from the slice.
/// This is done without `Parse` trait to have better perfomance.
#[inline(always)]
#[deprecated = "use scan_len instead"]
pub fn parse_len(slice: &[u8]) -> Result<Len, Error> {
    Ok(match slice.first() {
        Some(0xFFu8) => U64::parse(&slice[1..])?.parsed_owned().to_len()?,
        Some(0xFEu8) => U32::parse(&slice[1..])?.parsed_owned().to_len()?,
        Some(0xFDu8) => U16::parse(&slice[1..])?.parsed_owned().to_len()?,
        Some(x) => Len {
            n: *x as u64,
            consumed: 1,
        },
        None => return Err(Error::MoreBytesNeeded),
    })
}

#[inline(always)]
/// Same as `parse_len` but mutates the `consumed` variable and returns only the value.
pub fn scan_len(slice: &[u8], consumed: &mut usize) -> Result<u64, Error> {
    let Some(first_byte) = slice.first() else {
        return Err(Error::MoreBytesNeeded);
    };

    if *first_byte < 0xFD {
        // Common case: single-byte varint (values 0-253)
        *consumed += 1;
        return Ok(*first_byte as u64);
    }

    match first_byte {
        0xFF => {
            if slice.len() < 9 {
                return Err(Error::MoreBytesNeeded);
            }
            let n = u64::from_le_bytes([
                slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7], slice[8],
            ]);
            if n > u32::MAX as u64 {
                *consumed += 9;
                Ok(n)
            } else {
                Err(Error::NonMinimalVarInt)
            }
        }
        0xFE => {
            if slice.len() < 5 {
                return Err(Error::MoreBytesNeeded);
            }
            let n = u32::from_le_bytes([slice[1], slice[2], slice[3], slice[4]]).into();
            if n > u16::MAX as u64 {
                *consumed += 5;
                Ok(n)
            } else {
                Err(Error::NonMinimalVarInt)
            }
        }
        0xFD => {
            if slice.len() < 3 {
                return Err(Error::MoreBytesNeeded);
            }
            let n = u16::from_le_bytes([slice[1], slice[2]]).into();
            if n >= 0xFD {
                *consumed += 3;
                Ok(n)
            } else {
                Err(Error::NonMinimalVarInt)
            }
        }
        _ => unreachable!(), // Already handled < 0xFD case above
    }
}

/// Decode a count or length that must fit in an addressable slice.
#[inline(always)]
pub(crate) fn scan_len_usize(slice: &[u8], consumed: &mut usize) -> Result<usize, Error> {
    usize::try_from(scan_len(slice, consumed)?).map_err(|_| Error::MoreBytesNeeded)
}

impl Len {
    /// The value encoded in this compact int
    pub fn n(&self) -> u64 {
        self.n
    }

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

    /// slice_len
    pub fn slice_len(&self) -> usize {
        self.consumed + self.n as usize
    }
}

#[allow(deprecated)]
#[cfg(test)]
mod test {
    use super::parse_len;
    use super::scan_len;
    use crate::{bsl::Len, Error};

    #[test]
    fn oversized_lengths_are_rejected_by_parsers() {
        use crate::{
            bsl::{Block, Script, TxIns, TxOuts, Witness},
            Parse,
        };

        for length in [1u64 << 32, (1u64 << 32) + 1, u64::MAX] {
            let mut bytes = vec![0xff];
            bytes.extend(length.to_le_bytes());
            // Enough bytes for a small count produced by a truncating conversion.
            bytes.extend([0; 100]);
            assert_eq!(Script::parse(&bytes), Err(Error::MoreBytesNeeded));
            assert_eq!(TxIns::parse(&bytes), Err(Error::MoreBytesNeeded));
            assert_eq!(TxOuts::parse(&bytes), Err(Error::MoreBytesNeeded));
            assert_eq!(Witness::parse(&bytes), Err(Error::MoreBytesNeeded));

            let mut block = vec![0; 80];
            block.extend(&bytes[..9]);
            block.extend(crate::test_common::GENESIS_TX);
            assert_eq!(Block::parse(&block), Err(Error::MoreBytesNeeded));

            let mut witness = vec![1];
            witness.extend(&bytes);
            assert_eq!(Witness::parse(&witness), Err(Error::MoreBytesNeeded));
        }
    }

    #[test]
    fn scan_length_respects_pointer_width() {
        for length in [0xfc, 0xfd, u32::MAX as u64] {
            let bytes = bitcoin::consensus::serialize(&bitcoin::VarInt(length));
            let mut consumed = 0;
            assert_eq!(
                super::scan_len_usize(&bytes, &mut consumed),
                Ok(length as usize)
            );
            assert_eq!(consumed, bytes.len());
        }
        for length in [1u64 << 32, u64::MAX] {
            let bytes = bitcoin::consensus::serialize(&bitcoin::VarInt(length));
            let result = super::scan_len_usize(&bytes, &mut 0);
            #[cfg(target_pointer_width = "32")]
            assert_eq!(result, Err(Error::MoreBytesNeeded));
            #[cfg(target_pointer_width = "64")]
            assert_eq!(result, Ok(length as usize));
        }
    }

    #[test]
    fn test_parse_len() {
        fn check(slice: &[u8], consumed: usize, n: u64) {
            assert_eq!(parse_len(slice), Ok(Len { consumed, n }));
        }

        assert_eq!(parse_len(&[]), Err(Error::MoreBytesNeeded));

        check(&[10u8], 1, 10);
        check(&[0xFCu8], 1, 0xFC);

        assert_eq!(parse_len(&[0xFDu8, 0xFC, 0]), Err(Error::NonMinimalVarInt));

        check(&[0xFDu8, 0xFD, 0x00], 3, 0xFD);
        check(&[0xFDu8, 0xFD, 0x33], 3, 0x33FD);
        check(&[0xFDu8, 0xFF, 0xF], 3, 0xFFF);
        check(&[10u8, 0u8], 1, 10);

        assert_eq!(parse_len(&[0xFDu8]), Err(Error::MoreBytesNeeded));
        assert_eq!(parse_len(&[0xFDu8, 0xFD]), Err(Error::MoreBytesNeeded));
        assert_eq!(parse_len(&[0xFEu8]), Err(Error::MoreBytesNeeded));
        check(&[0xFEu8, 0xF, 0xF, 0xF, 0xF], 5, 0xF0F0F0F);
        check(
            &[0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0],
            9,
            0xF0F0F0F0F0E0,
        );

        assert_eq!(parse_len(&[0xFFu8]), Err(Error::MoreBytesNeeded));
    }

    #[test]
    fn test_scan_len() {
        let mut consumed = 0;
        assert_eq!(scan_len(&[], &mut consumed), Err(Error::MoreBytesNeeded));

        consumed = 0;
        assert_eq!(scan_len(&[10u8], &mut consumed), Ok(10));
        assert_eq!(consumed, 1);

        consumed = 0;
        assert_eq!(scan_len(&[0xFCu8], &mut consumed), Ok(0xFC));
        assert_eq!(consumed, 1);

        consumed = 0;
        assert_eq!(
            scan_len(&[0xFDu8, 0xFC, 0], &mut consumed),
            Err(Error::NonMinimalVarInt)
        );

        consumed = 0;
        assert_eq!(scan_len(&[0xFDu8, 0xFD, 0x00], &mut consumed), Ok(0xFD));
        assert_eq!(consumed, 3);

        consumed = 0;
        assert_eq!(scan_len(&[0xFDu8, 0xFD, 0x33], &mut consumed), Ok(0x33FD));
        assert_eq!(consumed, 3);

        consumed = 0;
        assert_eq!(scan_len(&[0xFDu8, 0xFF, 0xF], &mut consumed), Ok(0xFFF));
        assert_eq!(consumed, 3);

        consumed = 0;
        assert_eq!(scan_len(&[10u8, 0u8], &mut consumed), Ok(10));
        assert_eq!(consumed, 1);

        consumed = 0;
        assert_eq!(
            scan_len(&[0xFDu8], &mut consumed),
            Err(Error::MoreBytesNeeded)
        );

        consumed = 0;
        assert_eq!(
            scan_len(&[0xFDu8, 0xFD], &mut consumed),
            Err(Error::MoreBytesNeeded)
        );

        consumed = 0;
        assert_eq!(
            scan_len(&[0xFEu8], &mut consumed),
            Err(Error::MoreBytesNeeded)
        );

        consumed = 0;
        assert_eq!(
            scan_len(&[0xFEu8, 0xF, 0xF, 0xF, 0xF], &mut consumed),
            Ok(0xF0F0F0F)
        );
        assert_eq!(consumed, 5);

        consumed = 0;
        assert_eq!(
            scan_len(
                &[0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0],
                &mut consumed
            ),
            Ok(0xF0F0F0F0F0E0)
        );
        assert_eq!(consumed, 9);
    }

    #[test]
    fn test_len_slice() {
        let slices = [
            &[0x01u8][..],                                               // small value
            &[0xFC][..],                                                 // max small value
            &[0xFD, 0xFD, 0x00][..],                                     // minimal FD encoding
            &[0xFD, 0xFF, 0xFF][..],                                     // max FD value
            &[0xFE, 0x00, 0x00, 0x01, 0x00][..],                         // minimal FE encoding
            &[0xFE, 0xFF, 0xFF, 0xFF, 0xFF][..],                         // max FE value
            &[0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00][..], // minimal FF encoding
            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..], // max FF value
            &[0xFE, 0x01, 0x02, 0x03, 0x04][..],                         // random asymmetry
            &[0xFE, 0x01, 0x02, 0x03, 0x04, 0x05][..], // slice longer than strictly needed
        ];
        let expected = [
            1u64,
            0xFC,
            0xFD,
            0xFFFF,
            0x00010000,
            0xFFFFFFFF,
            0x0000000100000000,
            0xFFFFFFFFFFFFFFFF,
            0x04030201,
            0x04030201,
        ];
        let expected_len = [1, 1, 3, 3, 5, 5, 9, 9, 5, 5];
        for (i, (s, (e, l))) in slices
            .iter()
            .zip(expected.into_iter().zip(expected_len.into_iter()))
            .enumerate()
        {
            let mut consumed = 0;
            assert_eq!(scan_len(&s[..], &mut consumed), Ok(e), "fails {i}");
            assert_eq!(consumed, l);

            assert_eq!(parse_len(&s[..]), Ok(Len { consumed: l, n: e }));
        }
    }

    #[cfg(target_pointer_width = "64")]
    #[test]
    fn size_of() {
        assert_eq!(std::mem::size_of::<Len>(), 16);
    }
}