//! ACPI Table Definitions.
//!
//! Defines standard formats for system ACPI tables.
//! Supports only ACPI version >= 2.0.
//! Fields corresponding to ACPI 1.0 are preceded with an underscore (`_`) and are not in use.
//!
//! ## License
//!
//! Copyright (C) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0

use crate::{
    error::AcpiError,
    signature::{self, ACPI_CHECKSUM_OFFSET},
};
use alloc::{boxed::Box, vec::Vec};
use patina::{
    base::SIZE_4GB,
    component::service::{
        Service,
        memory::{AllocationOptions, MemoryManager, PageAllocationStrategy},
    },
    efi_types::EfiMemoryType,
    uefi_size_to_pages,
};

use core::{
    any::TypeId,
    fmt::Debug,
    mem,
    mem::ManuallyDrop,
    ptr::{self, NonNull},
    slice,
};

/// Represents the FADT for ACPI 2.0+.
/// Equivalent to EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE.
#[repr(C, packed)]
#[derive(Default)]
pub(crate) struct AcpiFadt {
    // Standard ACPI header.
    pub(crate) header: AcpiTableHeader,
    // Inner FADT data.
    pub(crate) inner: FadtData,
}

impl Clone for AcpiFadt {
    fn clone(&self) -> Self {
        Self { header: self.header().clone(), inner: self.inner().clone() }
    }
}

impl Debug for AcpiFadt {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("AcpiFadt").field("header", &self.header()).field("inner", &self.inner()).finish()
    }
}

/// Reads unaligned fields on the FADT.
/// Fields on the FADT may be unaligned, since by specification the FADT is packed.
impl AcpiFadt {
    pub fn header(&self) -> AcpiTableHeader {
        // SAFETY: `self.header` is always a valid, initialized ACPI header.
        unsafe { ptr::read_unaligned(ptr::addr_of!(self.header)) }
    }

    pub fn inner(&self) -> FadtData {
        // SAFETY: `self.inner` is always a valid, initialized FADT data structure.
        unsafe { ptr::read_unaligned(ptr::addr_of!(self.inner)) }
    }

    pub(crate) fn x_firmware_ctrl(&self) -> u64 {
        self.inner.x_firmware_ctrl
    }

    pub(crate) fn x_dsdt(&self) -> u64 {
        self.inner.x_dsdt
    }

    pub(crate) fn set_x_firmware_ctrl(&mut self, address: u64) {
        self.inner.x_firmware_ctrl = address;
    }

    pub(crate) fn set_x_dsdt(&mut self, address: u64) {
        self.inner.x_dsdt = address;
    }
}

#[repr(C, packed)]
#[derive(Default, Clone, Debug)]
pub(crate) struct FadtData {
    pub(crate) _firmware_ctrl: u32,
    pub(crate) _dsdt: u32,
    pub(crate) _reserved0: u8,

    pub(crate) preferred_pm_profile: u8,
    pub(crate) sci_int: u16,
    pub(crate) smi_cmd: u32,
    pub(crate) acpi_enable: u8,
    pub(crate) acpi_disable: u8,
    pub(crate) s4bios_req: u8,
    pub(crate) pstate_cnt: u8,
    pub(crate) pm1a_evt_blk: u32,
    pub(crate) pm1b_evt_blk: u32,
    pub(crate) pm1a_cnt_blk: u32,
    pub(crate) pm1b_cnt_blk: u32,
    pub(crate) pm2_cnt_blk: u32,
    pub(crate) pm_tmr_blk: u32,
    pub(crate) gpe0_blk: u32,
    pub(crate) gpe1_blk: u32,
    pub(crate) pm1_evt_len: u8,
    pub(crate) pm1_cnt_len: u8,
    pub(crate) pm2_cnt_len: u8,
    pub(crate) pm_tmr_len: u8,
    pub(crate) gpe0_blk_len: u8,
    pub(crate) gpe1_blk_len: u8,
    pub(crate) gpe1_base: u8,
    pub(crate) cst_cnt: u8,
    pub(crate) p_lvl2_lat: u16,
    pub(crate) p_lvl3_lat: u16,
    pub(crate) flush_size: u16,
    pub(crate) flush_stride: u16,
    pub(crate) duty_offset: u8,
    pub(crate) duty_width: u8,
    pub(crate) day_alrm: u8,
    pub(crate) mon_alrm: u8,
    pub(crate) century: u8,
    pub(crate) ia_pc_boot_arch: u16,
    pub(crate) reserved1: u8,
    pub(crate) flags: u32,
    pub(crate) reset_reg: GenericAddressStructure,
    pub(crate) reset_value: u8,
    pub(crate) reserved2: [u8; 3],

    /// Addresses of the FACS and DSDT (64-bit)
    pub(crate) x_firmware_ctrl: u64,
    pub(crate) x_dsdt: u64,

    pub(crate) x_pm1a_evt_blk: GenericAddressStructure,
    pub(crate) x_pm1b_evt_blk: GenericAddressStructure,
    pub(crate) x_pm1a_cnt_blk: GenericAddressStructure,
    pub(crate) x_pm1b_cnt_blk: GenericAddressStructure,
    pub(crate) x_pm2_cnt_blk: GenericAddressStructure,
    pub(crate) x_pm_tmr_blk: GenericAddressStructure,
    pub(crate) x_gpe0_blk: GenericAddressStructure,
    pub(crate) x_gpe1_blk: GenericAddressStructure,
}

/// Represents an ACPI address space for ACPI 2.0+.
/// Equivalent to EFI_ACPI_3_0_GENERIC_ADDRESS_STRUCTURE.
#[repr(C, packed)]
#[derive(Debug, Clone, Default, Copy)]
pub struct GenericAddressStructure {
    address_space_id: u8,
    register_bit_width: u8,
    register_bit_offset: u8,
    access_size: u8,
    address: u64,
}

/// Represents the FACS for ACPI 2.0+.
/// Note that the FACS does not have a standard ACPI header.
/// The FACS is not present in the list of installed ACPI tables; instead, it is only accessible through the FADT's `x_firmware_ctrl` field.
/// The FACS is always allocated in NVS, and is required to be 64B-aligned.
/// Equivalent to EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE.
#[repr(C, packed)]
#[derive(Default, Clone)]
pub struct AcpiFacs {
    pub(crate) signature: u32,
    pub(crate) length: u32,
    pub(crate) hardware_signature: u32,

    pub(crate) _firmware_waking_vector: u32,

    pub(crate) global_lock: u32,
    pub(crate) flags: u32,
    pub(crate) x_firmware_waking_vector: u64,
    pub(crate) version: u8,
    pub(crate) reserved: [u8; 31],
}

/// Represents the DSDT for ACPI 2.0+.
/// The DSDT is not present in the list of installed ACPI tables; instead, it is only accessible through the FADT's `x_dsdt` field.
/// The DSDT has a standard header followed by variable-length AML bytecode.
/// The `length` field of the header tells us the number of trailing bytes representing bytecode.
#[repr(C, packed)]
#[derive(Default)]
pub struct AcpiDsdt {
    pub(crate) header: AcpiTableHeader,
}

/// Represents the RSDP for ACPI 2.0+.
/// The RSDP is not a standard ACPI table and does not have a standard header.
/// It is not present in the list of installed tables and is not directly accessible.
/// Equivalent to EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER.
#[repr(C, packed)]
#[derive(Default)]
pub struct AcpiRsdp {
    pub(crate) signature: u64,

    pub(crate) checksum: u8,

    pub(crate) oem_id: [u8; 6],
    pub(crate) revision: u8,

    pub(crate) _rsdt_address: u32,

    pub(crate) length: u32,
    pub(crate) xsdt_address: u64,
    pub(crate) extended_checksum: u8,
    pub(crate) reserved: [u8; 3],
}

/// Represents the XSDT for ACPI 2.0+.
/// The XSDT has a standard header followed by 64-bit addresses of installed tables.
/// The `length` field of the header tells us the number of trailing bytes representing table entries.
#[repr(C, packed)]
#[derive(Default)]
pub struct AcpiXsdt {
    pub(crate) header: AcpiTableHeader,
}

/// Stores implementation-specific data about the XSDT.
pub(crate) struct AcpiXsdtMetadata {
    pub(crate) n_entries: usize,
    pub(crate) max_capacity: usize,
    pub(crate) slice: Box<[u8], &'static dyn alloc::alloc::Allocator>,
}

impl AcpiXsdtMetadata {
    // Get the 4-byte length (bytes 4..8 of the header).
    pub(crate) fn get_length(&self) -> Result<u32, AcpiError> {
        // XSDT always starts with header.
        let length_offset = mem::offset_of!(AcpiTableHeader, length);
        // Grab the current length from the correct offset in the header.
        self.slice
            .get(length_offset..length_offset + mem::size_of::<u32>()) // Length is a u32
            .and_then(|b| b.try_into().ok())
            .map(u32::from_le_bytes)
            .ok_or(AcpiError::XsdtOverflow)
    }

    // Set the 4-byte length (bytes 4..8 of the header).
    pub(crate) fn set_length(&mut self, new_len: u32) {
        // XSDT always starts with header.
        let length_offset = mem::offset_of!(AcpiTableHeader, length);
        self.slice
            .get_mut(length_offset..length_offset + mem::size_of::<u32>())
            .expect("slice contains a full ACPI header")
            .copy_from_slice(&new_len.to_le_bytes());
    }

    /// Set the 6-byte OEM ID (bytes 10..16 of the header).
    pub(crate) fn set_oem_id(&mut self, new_id: [u8; 6]) {
        let offset = mem::offset_of!(AcpiTableHeader, oem_id);
        let end = offset + mem::size_of::<[u8; 6]>();
        self.slice.get_mut(offset..end).expect("slice contains a full ACPI header").copy_from_slice(&new_id);
    }

    /// Set the 8-byte OEM Table ID (bytes 16..24 of the header).
    pub(crate) fn set_oem_table_id(&mut self, new_table_id: [u8; 8]) {
        let offset = mem::offset_of!(AcpiTableHeader, oem_table_id);
        let end = offset + mem::size_of::<[u8; 8]>();
        self.slice.get_mut(offset..end).expect("slice contains a full ACPI header").copy_from_slice(&new_table_id);
    }

    /// Set the 4-byte OEM Revision (bytes 24..28 of the header).
    pub(crate) fn set_oem_revision(&mut self, new_rev: u32) {
        let offset = mem::offset_of!(AcpiTableHeader, oem_revision);
        let end = offset + mem::size_of::<u32>();
        self.slice
            .get_mut(offset..end)
            .expect("slice contains a full ACPI header")
            .copy_from_slice(&new_rev.to_le_bytes());
    }
}

/// Represents a standard ACPI header.
/// Equivalent to EFI_ACPI_DESCRIPTION_HEADER.
#[repr(C, packed)]
#[derive(Default, Clone, Debug)]
pub struct AcpiTableHeader {
    pub signature: u32,
    pub length: u32,
    pub revision: u8,
    pub checksum: u8,
    pub oem_id: [u8; 6],
    pub oem_table_id: [u8; 8],
    pub oem_revision: u32,
    pub creator_id: u32,
    pub creator_revision: u32,
}

impl AcpiTableHeader {
    /// Reads the 4-byte signature field from a raw `AcpiTableHeader` pointer.
    ///
    /// # Safety
    ///
    /// `ptr` must point to a region of at least 4 bytes that is valid for reads.
    pub unsafe fn read_signature_from_ptr(ptr: *const Self) -> u32 {
        // SAFETY: Caller guarantees `ptr` points to at least 4 readable bytes.
        unsafe { ptr::read_unaligned(ptr::addr_of!((*ptr).signature)) }
    }

    /// Reads the 4-byte length field from a raw `AcpiTableHeader` pointer.
    ///
    /// # Safety
    ///
    /// `ptr` must point to a region of at least 8 bytes that is valid for reads.
    pub unsafe fn read_length_from_ptr(ptr: *const Self) -> u32 {
        // SAFETY: Caller guarantees `ptr` points to at least 8 readable bytes.
        unsafe { ptr::read_unaligned(ptr::addr_of!((*ptr).length)) }
    }

    /// Serialize an `AcpiTableHeader` into a `Vec<u8>` in ACPI's canonical layout.
    pub fn hdr_to_bytes(&self) -> Vec<u8> {
        // Pre‑allocate exactly the right length
        let mut buf = Vec::with_capacity(mem::size_of::<Self>());

        // Signature (4 bytes)
        buf.extend_from_slice(&self.signature.to_le_bytes());

        // Length (4 bytes, little‑endian)
        buf.extend_from_slice(&self.length.to_le_bytes());

        // Revision (1 byte), Checksum (1 byte)
        buf.push(self.revision);
        buf.push(self.checksum);

        // OEM ID (6 bytes)
        buf.extend_from_slice(&self.oem_id);

        // OEM Table ID (8 bytes)
        buf.extend_from_slice(&self.oem_table_id);

        // OEM Revision (4 bytes, little‑endian)
        buf.extend_from_slice(&self.oem_revision.to_le_bytes());

        // Creator ID (4 bytes, little‑endian)
        buf.extend_from_slice(&self.creator_id.to_le_bytes());

        // Creator Revision (4 bytes, little‑endian)
        buf.extend_from_slice(&self.creator_revision.to_le_bytes());

        buf
    }

    pub fn signature(&self) -> u32 {
        self.signature
    }

    pub fn length(&self) -> u32 {
        self.length
    }

    pub fn oem_revision(&self) -> u32 {
        self.oem_revision
    }

    pub fn creator_id(&self) -> u32 {
        self.creator_id
    }

    pub fn creator_revision(&self) -> u32 {
        self.creator_revision
    }
}

/// The inner table structure.
pub(crate) union Table<T = AcpiTableHeader> {
    /// The signature of the ACPI table.
    signature: u32,
    /// The header of the ACPI table.
    header: ManuallyDrop<AcpiTableHeader>,
    /// The full ACPI table, represented as its original type.
    pub(crate) inner: ManuallyDrop<T>,
}

impl<T> Table<T> {
    /// Creates a new table.
    ///
    /// ## Safety
    ///
    /// - Caller must ensure the provided table, `T`, has a C compatible layout (typically using `#[repr(C)]`).
    /// - Caller must ensure that the table's first field is [AcpiTableHeader].
    pub unsafe fn new(table: T) -> Result<Self, AcpiError> {
        let returned_table = Table { inner: ManuallyDrop::new(table) };

        // Make sure all bytes are valid ASCII.
        // By spec, ACPI table signatures are length-4 ASCII strings (represented numerically as u32's).
        let is_valid_ascii = returned_table.signature().to_le_bytes().iter().all(|b| b.is_ascii());
        if !is_valid_ascii {
            return Err(AcpiError::InvalidTableFormat);
        }

        // Make sure length is valid for type T.
        // SAFETY: If function preconditions are met, the header is valid and has a valid length.
        if (returned_table.header().length as usize) < mem::size_of::<T>() {
            return Err(AcpiError::InvalidTableFormat);
        }

        Ok(returned_table)
    }

    /// Returns the signature of the ACPI table.
    pub fn signature(&self) -> u32 {
        // SAFETY: [Self::new] ensures that the first field is a u32.
        unsafe { self.signature }
    }

    pub fn header(&self) -> &AcpiTableHeader {
        // SAFETY: [Self::new] ensures that the header is a valid AcpiTableHeader.
        unsafe { &self.header }
    }

    /// Returns an immutable reference to the entire table.
    pub fn as_ref(&self) -> &T {
        // SAFETY: [Self::new] ensures the inner object is a valid instance of `T`.
        unsafe { &self.inner }
    }

    /// Returns an immutable reference to the entire table.
    pub fn as_mut(&mut self) -> &mut T {
        // SAFETY: [Self::new] ensures the inner object is a valid instance of `T`.
        unsafe { &mut self.inner }
    }
}

#[derive(Clone, Debug)]
pub struct AcpiTable {
    pub(crate) table: NonNull<Table>,
    pub(crate) type_id: core::any::TypeId,
}

impl AcpiTable {
    /// Creates a new AcpiTable from a given table.
    ///
    /// For this function, the header `length` field must exactly equal `size_of::<T>()`. Because
    /// `table` is passed by value, only `size_of::<T>()` bytes are guaranteed to be available.
    /// A `length` that exceeds the struct size could cause an out-of-bounds copy in the underlying
    /// allocation.
    ///
    /// Tables with trailing variable-length data (e.g. header followed by AML bytecode) must be
    /// installed through the pointer-based [`Self::new_from_ptr`] path instead, where the caller
    /// guarantees that `length` bytes are readable at the source table address.
    ///
    /// ## Safety
    ///
    /// - Caller must ensure the provided table, `T`, has a C compatible layout (typically using `#[repr(C)]`).
    /// - Caller must ensure that the table's first field is [AcpiTableHeader].
    pub unsafe fn new<T: 'static>(table: T, mm: &Service<dyn MemoryManager>) -> Result<Self, AcpiError> {
        // When T is provided by value, enforce that the table length is equal to `size_of::<T>()`.
        // SAFETY: Caller guarantees T starts with an AcpiTableHeader and has a C-compatible layout.
        let length =
            unsafe { AcpiTableHeader::read_length_from_ptr(&table as *const T as *const AcpiTableHeader) } as usize;
        if length != mem::size_of::<T>() {
            return Err(AcpiError::InvalidTableFormat);
        }

        // SAFETY: If the caller preconditions are met, the signature, header, and table fields of the union are valid.
        let table = unsafe { Table::new(table) }?;

        // SAFETY: If caller preconditions are met, the table is valid and points to a valid ACPI table header.
        unsafe {
            AcpiTable::new_from_ptr(table.as_ref() as *const T as *const AcpiTableHeader, Some(TypeId::of::<T>()), mm)
        }
    }

    /// Creates a new AcpiTable from a raw pointer.
    /// When created this way, the type of the table is unknown.
    ///
    /// ## Safety
    ///
    /// - Caller must ensure the pointer refers to a valid ACPI table.
    /// - Caller must ensure `table_length` correctly specify the length of the table, including the header and any trailing data bytes.
    pub(crate) unsafe fn new_from_ptr(
        header_ptr: *const AcpiTableHeader,
        type_id: Option<TypeId>,
        mm: &Service<dyn MemoryManager>,
    ) -> Result<Self, AcpiError> {
        if header_ptr.is_null() {
            return Err(AcpiError::NullTablePtr);
        }

        // SAFETY: If function preconditions are met, the pointer is valid and points to a valid ACPI table header.
        let (table_signature, table_length) = unsafe { ((*header_ptr).signature, (*header_ptr).length as usize) };

        // FACS and UEFI tables must always be located in NVS (by spec).
        let allocator_type = match table_signature {
            signature::FACS | signature::UEFI => EfiMemoryType::ACPIMemoryNVS,
            _ => EfiMemoryType::ACPIReclaimMemory,
        };

        // The current Windows implementation uses the legacy 32-bit FACS pointer in the FADT.
        // As such, the FACS must be allocated in the lower 32-bit address space.
        // This workaround can be removed when Windows no longer relies on this field.
        let allocation_strategy = if table_signature == signature::FACS {
            PageAllocationStrategy::MaxAddress(SIZE_4GB - 1)
        } else {
            PageAllocationStrategy::Any
        };

        // Allocate memory in appropriate ACPI region, up to page granularity.
        let alloc_options =
            AllocationOptions::new().with_memory_type(allocator_type).with_strategy(allocation_strategy);
        let table_page_alloc = mm
            .allocate_pages(uefi_size_to_pages!(table_length), alloc_options)
            .map_err(|_e| AcpiError::AllocationFailed)?;

        // Get the raw pointer to the allocated memory for copying.
        let dest_alloc = table_page_alloc.into_raw_ptr().ok_or(AcpiError::AllocationFailed)?;

        // Copy entire table into the new allocation.
        // SAFETY: If function preconditions are met, the pointer is valid and points to a valid ACPI table header.
        // SAFETY: If function preconditions are met, the table length is guaranteed to be correct.
        // SAFETY: If allocation succeeds, the destination is valid for writes of `table_length` bytes.
        unsafe {
            ptr::copy_nonoverlapping(header_ptr as *const u8, dest_alloc, table_length);
        }

        // Leak the allocated bytes.
        let table = NonNull::new(dest_alloc.cast::<Table>()).ok_or(AcpiError::NullTablePtr)?;

        // Store the table type for convenience.
        // If the type is unknown (for example, coming over C FFI interface), use AcpiTableHeader as a fallback.
        let type_id = type_id.unwrap_or(TypeId::of::<AcpiTableHeader>());

        Ok(Self { table, type_id })
    }

    pub fn signature(&self) -> u32 {
        // SAFETY: The table is guaranteed to be a valid ACPI table.
        unsafe { self.table.as_ref().signature() }
    }

    pub fn header(&self) -> &AcpiTableHeader {
        // SAFETY: The table is guaranteed to be a valid ACPI table.
        unsafe { &self.table.as_ref().header }
    }

    /// Returns the bytes of the entire table, allocated on the heap.
    /// These bytes do not refer to the original table memory, and should not be accessed as such.
    ///
    /// ## SAFETY
    /// `self.length` must accurately reflect the allocated size of the table.
    pub unsafe fn as_bytes(&self) -> Vec<u8> {
        // SAFETY: If the caller preconditions are met, the length is accurate.
        unsafe { slice::from_raw_parts(self.table.as_ptr() as *const u8, self.header().length as usize).to_vec() }
    }

    /// Returns a mutable byte slice over the entire table.
    /// (This is primarily useful for computing the checksum.)
    ///
    /// ## SAFETY
    /// `self.length` must accurately reflect the allocated size of the table.
    pub(crate) unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
        // SAFETY: If the caller preconditions are met, the length is accurate.
        unsafe { slice::from_raw_parts_mut(self.table.as_ptr() as *mut u8, self.header().length as usize) }
    }

    /// Updates the checksum for an ACPI table.
    /// According to the ACPI spec 2.0+, all bytes of a table must sum to zero modulo 256.
    pub(crate) fn update_checksum(&mut self) -> Result<(), AcpiError> {
        // SAFETY: The construction of `AcpiTable` maintains that `self.length` is the size in memory.
        let bytes = unsafe { self.as_bytes_mut() };

        // Set the checksum field to zero before recalculation.
        let checksum_byte = bytes.get_mut(ACPI_CHECKSUM_OFFSET).ok_or(AcpiError::InvalidChecksumOffset)?;
        *checksum_byte = 0;

        // Recalculate checksum.
        let sum: u8 = bytes.iter().fold(0u8, |sum, &b| sum.wrapping_add(b));
        let checksum_byte = bytes.get_mut(ACPI_CHECKSUM_OFFSET).ok_or(AcpiError::InvalidChecksumOffset)?;
        *checksum_byte = (0u8).wrapping_sub(sum);
        Ok(())
    }

    /// Returns a reference to the entire AcpiTable.
    ///
    /// ## Safety
    ///
    /// - Caller must ensure that the provided table format is the same as `T`.
    pub unsafe fn as_ref<T>(&self) -> &T {
        // SAFETY: Caller must ensure that the provided table format is the same as `T`.
        unsafe { self.table.cast::<Table<T>>().as_ref().as_ref() }
    }

    /// Returns a mutable reference to the entire AcpiTable.
    ///
    /// ## Safety
    ///
    /// - Caller must ensure that the provided table format is the same as `T`.
    pub(crate) unsafe fn as_mut<T>(&mut self) -> &mut T {
        // SAFETY: Caller must ensure that the provided table format is the same as `T`.
        unsafe { self.table.cast::<Table<T>>().as_mut().as_mut() }
    }

    /// Returns a pointer to the underlying AcpiTable.
    pub(crate) fn as_ptr(&self) -> *const AcpiTableHeader {
        self.table.as_ptr() as *const AcpiTableHeader
    }

    /// Returns a mutable pointer to the underlying AcpiTable.
    pub(crate) fn as_mut_ptr(&self) -> *mut AcpiTableHeader {
        self.table.as_ptr() as *mut AcpiTableHeader
    }
}

#[cfg(test)]
mod tests {
    use patina::component::service::memory::StdMemoryManager;

    use super::*;
    use core::{mem, ptr::NonNull};

    #[repr(C)]
    struct TestTable {
        header: AcpiTableHeader,
        body: [u8; 3],
    }

    const TEST_SIGNATURE: u32 = 0x123;

    #[test]
    fn test_update_checksum_on_real_acpi_table() {
        // Build a mock table.
        let test_table = TestTable {
            header: AcpiTableHeader {
                signature: TEST_SIGNATURE,
                length: (mem::size_of::<TestTable>()) as u32,
                revision: 1,
                checksum: 0, // we'll fill this
                oem_id: [0; 6],
                oem_table_id: *b"TBL_ID__",
                oem_revision: 0xAABBCCDD,
                creator_id: 0x11223344,
                creator_revision: 0x55667788,
            },
            body: [10, 20, 30], // some payload bytes
        };

        // Set up the test table.
        // SAFETY: `test_table` has the correct format by definition (above).
        let table_union: Table<TestTable> = unsafe { Table::new(test_table).unwrap() };
        // Box it on the heap (uses the global allocator).
        let boxed: Box<Table<TestTable>> = Box::new(table_union);
        let raw_ptr: *mut Table<TestTable> = Box::into_raw(boxed);
        // SAFETY: This is set up to be non-null by the test.
        let nn = unsafe { NonNull::new_unchecked(raw_ptr as *mut Table) };

        // Wrap in AcpiTable.
        let mut acpi_table = AcpiTable { table: nn, type_id: TypeId::of::<TestTable>() };

        // Update the checksum (use standard checksum offset since it has a standard header).
        assert!(acpi_table.update_checksum().is_ok());

        // Pull out the bytes and verify the checksum.
        // SAFETY: The table length is correctly specified in the test header.
        let bytes = unsafe { acpi_table.as_bytes() };
        // Total sum must be zero mod 256.
        let total: u8 = bytes.iter().copied().fold(0u8, |acc, b| acc.wrapping_add(b));
        assert_eq!(total, 0, "entire table did not sum to zero");
    }

    #[test]
    fn test_new_from_ptr_creates_valid_acpi_table() {
        // Build a mock table.
        let test_table = TestTable {
            header: AcpiTableHeader {
                signature: TEST_SIGNATURE,
                length: (mem::size_of::<TestTable>()) as u32,
                revision: 2,
                checksum: 0,
                oem_id: [1, 2, 3, 4, 5, 6],
                oem_table_id: *b"test_tes",
                oem_revision: 0xDEADBEEF,
                creator_id: 0xCAFEBABE,
                creator_revision: 0xFEEDFACE,
            },
            body: [42, 43, 44],
        };

        // Allocate the table on the heap.
        let boxed = Box::new(test_table);
        let raw_ptr = Box::into_raw(boxed);

        let mm: Service<dyn MemoryManager> = Service::mock(Box::new(StdMemoryManager::new()));

        // SAFETY: raw_ptr points to a valid TestTable with a valid header.
        let acpi_table =
            unsafe { AcpiTable::new_from_ptr(raw_ptr as *const AcpiTableHeader, Some(TypeId::of::<TestTable>()), &mm) }
                .unwrap();

        // Check signature and header fields.
        assert_eq!(acpi_table.signature(), TEST_SIGNATURE);
        let header = acpi_table.header();
        assert_eq!(header.length(), mem::size_of::<TestTable>() as u32);
        assert_eq!(header.revision, 2);
        assert_eq!(header.oem_id, [1, 2, 3, 4, 5, 6]);
        assert_eq!(header.oem_table_id, *b"test_tes");
        assert_eq!(header.oem_revision(), 0xDEADBEEF);
        assert_eq!(header.creator_id(), 0xCAFEBABE);
        assert_eq!(header.creator_revision(), 0xFEEDFACE);
        // SAFETY: The table type `TestTable` is constructed by the test.
        assert_eq!(unsafe { acpi_table.as_ref::<TestTable>().body }, [42, 43, 44]);

        // Check that the body bytes are correct.
        // SAFETY: The length and table are constructed by the test.
        let bytes = unsafe { acpi_table.as_bytes() };
        let body_offset = mem::size_of::<AcpiTableHeader>();
        assert_eq!(&bytes[body_offset..body_offset + 3], &[42, 43, 44]);

        // Verify new_from_ptr correctly copies trailing data beyond the struct.
        let trailing: &[u8] = &[0xAA, 0xBB, 0xCC, 0xDD];
        let struct_size = mem::size_of::<TestTable>();
        let total_len = struct_size + trailing.len();
        let mut buf = vec![0u8; total_len];

        // SAFETY: raw_ptr still points to the heap-allocated TestTable from above.
        unsafe {
            ptr::copy_nonoverlapping(raw_ptr as *const u8, buf.as_mut_ptr(), struct_size);
        }
        // Follow up with the trailing data.
        buf[struct_size..].copy_from_slice(trailing);
        // Patch the length field to cover the trailing data.
        buf[4..8].copy_from_slice(&(total_len as u32).to_le_bytes());

        // SAFETY: buf points to a contiguous buffer of total_len bytes with a valid header.
        let table_with_trailing =
            unsafe { AcpiTable::new_from_ptr(buf.as_ptr() as *const AcpiTableHeader, None, &mm) }.unwrap();
        assert_eq!(table_with_trailing.header().length(), total_len as u32);

        // SAFETY: The length is set correctly by the test.
        let all_bytes = unsafe { table_with_trailing.as_bytes() };
        assert_eq!(&all_bytes[struct_size..], trailing);
    }

    #[test]
    fn test_new_rejects_length_greater_than_struct_size() {
        let header = AcpiTableHeader { signature: TEST_SIGNATURE, length: 100, ..Default::default() };
        let mm: Service<dyn MemoryManager> = Service::mock(Box::new(StdMemoryManager::new()));

        // length > size_of::<AcpiTableHeader>(), so it should be rejected.
        // SAFETY: The header has a valid layout. This tests that the length mismatch is caught.
        let result = unsafe { AcpiTable::new(header, &mm) };
        assert!(matches!(result, Err(AcpiError::InvalidTableFormat)));
    }

    #[test]
    fn test_new_rejects_length_less_than_struct_size() {
        let header = AcpiTableHeader { signature: TEST_SIGNATURE, length: 10, ..Default::default() };
        let mm: Service<dyn MemoryManager> = Service::mock(Box::new(StdMemoryManager::new()));

        // length < size_of::<AcpiTableHeader>(), so it should be rejected.
        // SAFETY: The header has a valid layout. This tests that the length mismatch is caught.
        let result = unsafe { AcpiTable::new(header, &mm) };
        assert!(matches!(result, Err(AcpiError::InvalidTableFormat)));
    }
}
