pci-info 0.3.4

A crate to enumerate PCI devices on desktop operating systems and/or parse PCI configuration headers
Documentation
#![allow(clippy::precedence)]

use crate::PciInfoError;

pub(super) struct PciConfigBuffer<'a> {
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> PciConfigBuffer<'a> {
    const REGISTER_SIZE: usize = std::mem::size_of::<u32>();

    pub fn new(bytes: &'a [u8], offset: usize) -> Self {
        assert!(offset % Self::REGISTER_SIZE == 0);

        Self { bytes, offset }
    }

    #[inline(always)]
    fn calc_base(&self, register: usize) -> usize {
        register * Self::REGISTER_SIZE - self.offset
    }

    #[inline(always)]
    pub fn read_u32(&self, register: usize) -> u32 {
        let base = self.calc_base(register);
        (self.bytes[base] as u32)
            | (self.bytes[base + 1] as u32) << 8
            | (self.bytes[base + 2] as u32) << 16
            | (self.bytes[base + 3] as u32) << 24
    }

    #[inline(always)]
    pub fn read_u16_lo(&self, register: usize) -> u16 {
        let base = self.calc_base(register);
        (self.bytes[base] as u16) | (self.bytes[base + 1] as u16) << 8
    }

    #[inline(always)]
    pub fn read_u16_hi(&self, register: usize) -> u16 {
        let base = self.calc_base(register);
        (self.bytes[base + 2] as u16) | (self.bytes[base + 3] as u16) << 8
    }

    #[inline(always)]
    pub fn read_u8(&self, register: usize, offset_in_register: usize) -> u8 {
        assert!(offset_in_register <= 3);
        self.bytes[self.calc_base(register) + offset_in_register]
    }

    pub fn assert_registers_available(
        &self,
        first_register: usize,
        last_register_including: usize,
    ) -> Result<(), PciInfoError> {
        if self.first_register() > first_register {
            return Err(PciInfoError::ParseError(
                "not enough bytes at start of header".into(),
            ));
        }

        if last_register_including >= self.last_register() {
            return Err(PciInfoError::ParseError(
                "not enough bytes at end of header".into(),
            ));
        }

        Ok(())
    }

    pub fn first_register(&self) -> usize {
        self.offset / Self::REGISTER_SIZE
    }

    pub fn last_register(&self) -> usize {
        self.offset + self.bytes.len() / Self::REGISTER_SIZE
    }
}