#include <uacpi/types.h>
#include <uacpi/acpi.h>
#include <uacpi/internal/resources.h>
#include <uacpi/internal/stdlib.h>
#include <uacpi/internal/utilities.h>
#include <uacpi/internal/log.h>
#include <uacpi/internal/namespace.h>
#include <uacpi/uacpi.h>

#ifndef UACPI_BAREBONES_MODE

#define LARGE_RESOURCE_BASE (ACPI_RESOURCE_END_TAG + 1)
#define L(x) (x + LARGE_RESOURCE_BASE)

/*
 * Map raw AML resource types to the internal enum, this also takes care of type
 * sanitization by returning UACPI_AML_RESOURCE_INVALID for any unknown type.
 */
static const uacpi_u8 aml_resource_to_type[256] = {
    // Small items
    [ACPI_RESOURCE_IRQ] = UACPI_AML_RESOURCE_IRQ,
    [ACPI_RESOURCE_DMA] = UACPI_AML_RESOURCE_DMA,
    [ACPI_RESOURCE_START_DEPENDENT] = UACPI_AML_RESOURCE_START_DEPENDENT,
    [ACPI_RESOURCE_END_DEPENDENT] = UACPI_AML_RESOURCE_END_DEPENDENT,
    [ACPI_RESOURCE_IO] = UACPI_AML_RESOURCE_IO,
    [ACPI_RESOURCE_FIXED_IO] = UACPI_AML_RESOURCE_FIXED_IO,
    [ACPI_RESOURCE_FIXED_DMA] = UACPI_AML_RESOURCE_FIXED_DMA,
    [ACPI_RESOURCE_VENDOR_TYPE0] = UACPI_AML_RESOURCE_VENDOR_TYPE0,
    [ACPI_RESOURCE_END_TAG] = UACPI_AML_RESOURCE_END_TAG,

    // Large items
    [L(ACPI_RESOURCE_MEMORY24)] = UACPI_AML_RESOURCE_MEMORY24,
    [L(ACPI_RESOURCE_GENERIC_REGISTER)] = UACPI_AML_RESOURCE_GENERIC_REGISTER,
    [L(ACPI_RESOURCE_VENDOR_TYPE1)] = UACPI_AML_RESOURCE_VENDOR_TYPE1,
    [L(ACPI_RESOURCE_MEMORY32)] = UACPI_AML_RESOURCE_MEMORY32,
    [L(ACPI_RESOURCE_FIXED_MEMORY32)] = UACPI_AML_RESOURCE_FIXED_MEMORY32,
    [L(ACPI_RESOURCE_ADDRESS32)] = UACPI_AML_RESOURCE_ADDRESS32,
    [L(ACPI_RESOURCE_ADDRESS16)] = UACPI_AML_RESOURCE_ADDRESS16,
    [L(ACPI_RESOURCE_EXTENDED_IRQ)] = UACPI_AML_RESOURCE_EXTENDED_IRQ,
    [L(ACPI_RESOURCE_ADDRESS64_EXTENDED)] = UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
    [L(ACPI_RESOURCE_ADDRESS64)] = UACPI_AML_RESOURCE_ADDRESS64,
    [L(ACPI_RESOURCE_GPIO_CONNECTION)] = UACPI_AML_RESOURCE_GPIO_CONNECTION,
    [L(ACPI_RESOURCE_PIN_FUNCTION)] = UACPI_AML_RESOURCE_PIN_FUNCTION,
    [L(ACPI_RESOURCE_SERIAL_CONNECTION)] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
    [L(ACPI_RESOURCE_PIN_CONFIGURATION)] = UACPI_AML_RESOURCE_PIN_CONFIGURATION,
    [L(ACPI_RESOURCE_PIN_GROUP)] = UACPI_AML_RESOURCE_PIN_GROUP,
    [L(ACPI_RESOURCE_PIN_GROUP_FUNCTION)] = UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
    [L(ACPI_RESOURCE_PIN_GROUP_CONFIGURATION)] = UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
    [L(ACPI_RESOURCE_CLOCK_INPUT)] = UACPI_AML_RESOURCE_CLOCK_INPUT,
};

static const uacpi_u8 type_to_aml_resource[] = {
    [UACPI_AML_RESOURCE_IRQ] = ACPI_RESOURCE_IRQ,
    [UACPI_AML_RESOURCE_DMA] = ACPI_RESOURCE_DMA,
    [UACPI_AML_RESOURCE_START_DEPENDENT] = ACPI_RESOURCE_START_DEPENDENT,
    [UACPI_AML_RESOURCE_END_DEPENDENT] = ACPI_RESOURCE_END_DEPENDENT,
    [UACPI_AML_RESOURCE_IO] = ACPI_RESOURCE_IO,
    [UACPI_AML_RESOURCE_FIXED_IO] = ACPI_RESOURCE_FIXED_IO,
    [UACPI_AML_RESOURCE_FIXED_DMA] = ACPI_RESOURCE_FIXED_DMA,
    [UACPI_AML_RESOURCE_VENDOR_TYPE0] = ACPI_RESOURCE_VENDOR_TYPE0,
    [UACPI_AML_RESOURCE_END_TAG] = ACPI_RESOURCE_END_TAG,

    // Large items
    [UACPI_AML_RESOURCE_MEMORY24] = ACPI_RESOURCE_MEMORY24,
    [UACPI_AML_RESOURCE_GENERIC_REGISTER] = ACPI_RESOURCE_GENERIC_REGISTER,
    [UACPI_AML_RESOURCE_VENDOR_TYPE1] = ACPI_RESOURCE_VENDOR_TYPE1,
    [UACPI_AML_RESOURCE_MEMORY32] = ACPI_RESOURCE_MEMORY32,
    [UACPI_AML_RESOURCE_FIXED_MEMORY32] = ACPI_RESOURCE_FIXED_MEMORY32,
    [UACPI_AML_RESOURCE_ADDRESS32] = ACPI_RESOURCE_ADDRESS32,
    [UACPI_AML_RESOURCE_ADDRESS16] = ACPI_RESOURCE_ADDRESS16,
    [UACPI_AML_RESOURCE_EXTENDED_IRQ] = ACPI_RESOURCE_EXTENDED_IRQ,
    [UACPI_AML_RESOURCE_ADDRESS64_EXTENDED] = ACPI_RESOURCE_ADDRESS64_EXTENDED,
    [UACPI_AML_RESOURCE_ADDRESS64] = ACPI_RESOURCE_ADDRESS64,
    [UACPI_AML_RESOURCE_GPIO_CONNECTION] = ACPI_RESOURCE_GPIO_CONNECTION,
    [UACPI_AML_RESOURCE_PIN_FUNCTION] = ACPI_RESOURCE_PIN_FUNCTION,
    [UACPI_AML_RESOURCE_SERIAL_CONNECTION] = ACPI_RESOURCE_SERIAL_CONNECTION,
    [UACPI_AML_RESOURCE_PIN_CONFIGURATION] = ACPI_RESOURCE_PIN_CONFIGURATION,
    [UACPI_AML_RESOURCE_PIN_GROUP] = ACPI_RESOURCE_PIN_GROUP,
    [UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION] = ACPI_RESOURCE_PIN_GROUP_FUNCTION,
    [UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION] = ACPI_RESOURCE_PIN_GROUP_CONFIGURATION,
    [UACPI_AML_RESOURCE_CLOCK_INPUT] = ACPI_RESOURCE_CLOCK_INPUT,
};

static const uacpi_u8 native_resource_to_type[UACPI_RESOURCE_TYPE_MAX + 1] = {
    [UACPI_RESOURCE_TYPE_IRQ] = UACPI_AML_RESOURCE_IRQ,
    [UACPI_RESOURCE_TYPE_EXTENDED_IRQ] = UACPI_AML_RESOURCE_EXTENDED_IRQ,
    [UACPI_RESOURCE_TYPE_DMA] = UACPI_AML_RESOURCE_DMA,
    [UACPI_RESOURCE_TYPE_FIXED_DMA] = UACPI_AML_RESOURCE_FIXED_DMA,
    [UACPI_RESOURCE_TYPE_IO] = UACPI_AML_RESOURCE_IO,
    [UACPI_RESOURCE_TYPE_FIXED_IO] = UACPI_AML_RESOURCE_FIXED_IO,
    [UACPI_RESOURCE_TYPE_ADDRESS16] = UACPI_AML_RESOURCE_ADDRESS16,
    [UACPI_RESOURCE_TYPE_ADDRESS32] = UACPI_AML_RESOURCE_ADDRESS32,
    [UACPI_RESOURCE_TYPE_ADDRESS64] = UACPI_AML_RESOURCE_ADDRESS64,
    [UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED] = UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
    [UACPI_RESOURCE_TYPE_MEMORY24] = UACPI_AML_RESOURCE_MEMORY24,
    [UACPI_RESOURCE_TYPE_MEMORY32] = UACPI_AML_RESOURCE_MEMORY32,
    [UACPI_RESOURCE_TYPE_FIXED_MEMORY32] = UACPI_AML_RESOURCE_FIXED_MEMORY32,
    [UACPI_RESOURCE_TYPE_START_DEPENDENT] = UACPI_AML_RESOURCE_START_DEPENDENT,
    [UACPI_RESOURCE_TYPE_END_DEPENDENT] = UACPI_AML_RESOURCE_END_DEPENDENT,
    [UACPI_RESOURCE_TYPE_VENDOR_SMALL] = UACPI_AML_RESOURCE_VENDOR_TYPE0,
    [UACPI_RESOURCE_TYPE_VENDOR_LARGE] = UACPI_AML_RESOURCE_VENDOR_TYPE1,
    [UACPI_RESOURCE_TYPE_GENERIC_REGISTER] = UACPI_AML_RESOURCE_GENERIC_REGISTER,
    [UACPI_RESOURCE_TYPE_GPIO_CONNECTION] = UACPI_AML_RESOURCE_GPIO_CONNECTION,
    [UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
    [UACPI_RESOURCE_TYPE_SERIAL_SPI_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
    [UACPI_RESOURCE_TYPE_SERIAL_UART_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
    [UACPI_RESOURCE_TYPE_SERIAL_CSI2_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
    [UACPI_RESOURCE_TYPE_PIN_FUNCTION] = UACPI_AML_RESOURCE_PIN_FUNCTION,
    [UACPI_RESOURCE_TYPE_PIN_CONFIGURATION] = UACPI_AML_RESOURCE_PIN_CONFIGURATION,
    [UACPI_RESOURCE_TYPE_PIN_GROUP] = UACPI_AML_RESOURCE_PIN_GROUP,
    [UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION] = UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
    [UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION] = UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
    [UACPI_RESOURCE_TYPE_CLOCK_INPUT] = UACPI_AML_RESOURCE_CLOCK_INPUT,
    [UACPI_RESOURCE_TYPE_END_TAG] = UACPI_AML_RESOURCE_END_TAG,
};

#define SMALL_ITEM_HEADER_SIZE sizeof(struct acpi_small_item)
#define LARGE_ITEM_HEADER_SIZE sizeof(struct acpi_large_item)

static const uacpi_u8 aml_resource_kind_to_header_size[2] = {
    [UACPI_AML_RESOURCE_KIND_SMALL] = SMALL_ITEM_HEADER_SIZE,
    [UACPI_AML_RESOURCE_KIND_LARGE] = LARGE_ITEM_HEADER_SIZE,
};

static uacpi_size aml_size_with_header(const struct uacpi_resource_spec *spec)
{
    return spec->aml_size +
           aml_resource_kind_to_header_size[spec->resource_kind];
}

static uacpi_size extra_size_for_native_irq_or_dma(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    uacpi_u16 mask;
    uacpi_u8 i, total_bits, num_bits = 0;

    UACPI_UNUSED(size);

    if (spec->type == UACPI_AML_RESOURCE_IRQ) {
        struct acpi_resource_irq *irq = data;
        mask = irq->irq_mask;
        total_bits = 16;
    } else {
        struct acpi_resource_dma *dma = data;
        mask = dma->channel_mask;
        total_bits = 8;
    }

    for (i = 0; i < total_bits; ++i)
        num_bits += !!(mask & (1 << i));

    return num_bits;
}

static uacpi_size size_for_aml_irq(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_resource_irq *irq = &resource->irq;
    uacpi_size size;

    size = aml_size_with_header(spec);

    switch (irq->length_kind) {
    case UACPI_RESOURCE_LENGTH_KIND_FULL:
        goto out_full;
    case UACPI_RESOURCE_LENGTH_KIND_ONE_LESS:
    case UACPI_RESOURCE_LENGTH_KIND_DONT_CARE:
        if (irq->triggering != UACPI_TRIGGERING_EDGE)
            goto out_full;
        if (irq->polarity != UACPI_POLARITY_ACTIVE_HIGH)
            goto out_full;
        if (irq->sharing != UACPI_EXCLUSIVE)
            goto out_full;

        return size - 1;
    }

out_full:
    if (uacpi_unlikely(irq->length_kind ==
                       UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)) {
        uacpi_warn("requested IRQ resource length is "
                   "not compatible with specified flags, corrected\n");
    }

    return size;
}

static uacpi_size size_for_aml_start_dependent(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_resource_start_dependent *start_dep = &resource->start_dependent;
    uacpi_size size;

    size = aml_size_with_header(spec);
    switch (start_dep->length_kind) {
    case UACPI_RESOURCE_LENGTH_KIND_FULL:
        goto out_full;
    case UACPI_RESOURCE_LENGTH_KIND_ONE_LESS:
    case UACPI_RESOURCE_LENGTH_KIND_DONT_CARE:
        if (start_dep->compatibility != UACPI_ACCEPTABLE)
            goto out_full;
        if (start_dep->performance != UACPI_ACCEPTABLE)
            goto out_full;

        return size - 1;
    }

out_full:
    if (uacpi_unlikely(start_dep->length_kind ==
                       UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)) {
        uacpi_warn("requested StartDependentFn resource length is "
                   "not compatible with specified flags, corrected\n");
    }

    return size;
}

static uacpi_size extra_size_for_native_vendor(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    UACPI_UNUSED(spec);
    UACPI_UNUSED(data);
    return size;
}

static uacpi_size size_for_aml_vendor(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_size size = resource->vendor.length;

    UACPI_UNUSED(spec);

    if (size > 7 || resource->type == UACPI_RESOURCE_TYPE_VENDOR_LARGE) {
        size += aml_resource_kind_to_header_size[
            UACPI_AML_RESOURCE_KIND_LARGE
        ];

        if (uacpi_unlikely(resource->type != UACPI_RESOURCE_TYPE_VENDOR_LARGE)) {
            uacpi_warn("vendor data too large for small descriptor (%zu), "
                       "correcting to large\n", size);
            resource->type = UACPI_RESOURCE_TYPE_VENDOR_LARGE;
        }
    } else {
        size += aml_resource_kind_to_header_size[
            UACPI_AML_RESOURCE_KIND_SMALL
        ];
    }

    return size;
}

static uacpi_size extra_size_for_resource_source(
    uacpi_size base_size, uacpi_size reported_size
)
{
    uacpi_size string_length;

    if (reported_size <= base_size)
        return 0;

    /*
     * The remainder of the descriptor minus the resource index field
     */
    string_length = (reported_size - base_size) - 1;
    return UACPI_ALIGN_UP(string_length, sizeof(void*), uacpi_size);
}

static uacpi_size size_for_aml_resource_source(
    uacpi_resource_source *source, uacpi_bool with_index
)
{
    uacpi_size length = source->length;

    if (uacpi_unlikely(length && !source->index_present)) {
        uacpi_warn("resource declares no source index with non-empty "
                   "string (%zu bytes), corrected\n", length);
        source->index_present = UACPI_TRUE;
    }

    // If index is included in the dynamic resource source, add it to the length
    if (with_index)
        length += source->index_present;

    return length;
}

static uacpi_size extra_size_for_native_address_or_clock_input(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    UACPI_UNUSED(data);
    return extra_size_for_resource_source(spec->aml_size, size);
}

static uacpi_size size_for_aml_address_or_clock_input(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_resource_source *source;
    bool has_index = UACPI_TRUE;

    switch (resource->type) {
    case UACPI_RESOURCE_TYPE_ADDRESS16:
        source = &resource->address16.source;
        break;
    case UACPI_RESOURCE_TYPE_ADDRESS32:
        source = &resource->address32.source;
        break;
    case UACPI_RESOURCE_TYPE_ADDRESS64:
        source = &resource->address64.source;
        break;
    case UACPI_RESOURCE_TYPE_CLOCK_INPUT:
        source = &resource->clock_input.source;
        has_index = UACPI_FALSE;
        break;
    default:
        return 0;
    }

    return aml_size_with_header(spec) +
           size_for_aml_resource_source(source, has_index);
}

static uacpi_size extra_size_for_extended_irq(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    struct acpi_resource_extended_irq *irq = data;
    uacpi_size extra_size = 0;

    extra_size += irq->num_irqs * sizeof(uacpi_u32);
    extra_size += extra_size_for_resource_source(
        spec->aml_size, size - extra_size
    );

    return extra_size;
}

static uacpi_size size_for_aml_extended_irq(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_resource_extended_irq *irq = &resource->extended_irq;
    uacpi_size size;

    size = aml_size_with_header(spec);
    size += irq->num_irqs * 4;
    size += size_for_aml_resource_source(&irq->source, UACPI_TRUE);

    return size;
}

static uacpi_size extra_size_for_native_gpio_or_pins(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    uacpi_size pin_table_offset;

    /*
     * These resources pretend to have variable layout by declaring "offset"
     * fields, but the layout is hardcoded and mandated by the spec to be
     * very specific. We can use the offset numbers here to calculate the final
     * length.
     *
     * For example, the layout of GPIO connection _always_ looks as follows:
     * [0...22] -> fixed data
     * [23...<source name offset - 1>] -> pin table
     * [<source name offset>...<vendor data offset - 1>] -> source name
     * [<vendor data offset>...<data offset + data length>] -> vendor data
     */
    switch (spec->type) {
    case UACPI_AML_RESOURCE_GPIO_CONNECTION: {
        struct acpi_resource_gpio_connection *gpio = data;
        pin_table_offset = gpio->pin_table_offset;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_FUNCTION: {
        struct acpi_resource_pin_function *pin = data;
        pin_table_offset = pin->pin_table_offset;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_CONFIGURATION: {
        struct acpi_resource_pin_configuration *config = data;
        pin_table_offset = config->pin_table_offset;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_GROUP: {
        struct acpi_resource_pin_group *group = data;
        pin_table_offset = group->pin_table_offset;
        break;
    }

    default:
        return 0;
    }

    /*
     * The size we get passed here does not include the header size because
     * that's how resources are encoded. Subtract it here so that we get the
     * correct final length.
     */
    return size - (pin_table_offset - LARGE_ITEM_HEADER_SIZE);
}

static uacpi_size size_for_aml_gpio_or_pins(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_size source_length, vendor_length, pin_table_length, size;

    size = aml_size_with_header(spec);
    switch (spec->type) {
    case UACPI_AML_RESOURCE_GPIO_CONNECTION: {
        uacpi_resource_gpio_connection *res = &resource->gpio_connection;
        source_length = res->source.length;
        pin_table_length = res->pin_table_length;
        vendor_length = res->vendor_data_length;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_FUNCTION: {
        uacpi_resource_pin_function *res = &resource->pin_function;
        source_length = res->source.length;
        pin_table_length = res->pin_table_length;
        vendor_length = res->vendor_data_length;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_CONFIGURATION: {
        uacpi_resource_pin_configuration *res = &resource->pin_configuration;
        source_length = res->source.length;
        pin_table_length = res->pin_table_length;
        vendor_length = res->vendor_data_length;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_GROUP: {
        uacpi_resource_pin_group *res = &resource->pin_group;
        source_length = res->label.length;
        pin_table_length = res->pin_table_length;
        vendor_length = res->vendor_data_length;
        break;
    }

    default:
        return 0;
    }

    size += source_length;
    size += pin_table_length * 2;
    size += vendor_length;

    return size;
}

static uacpi_size extra_size_for_native_pin_group(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    uacpi_size source_offset;

    switch (spec->type) {
    case UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION: {
        struct acpi_resource_pin_group_function *func = data;
        source_offset = func->source_offset;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION: {
        struct acpi_resource_pin_group_configuration *config = data;
        source_offset = config->source_offset;
        break;
    }

    default:
        return 0;
    }

    // Same logic as extra_size_for_native_gpio_or_pins
    return size - (source_offset - LARGE_ITEM_HEADER_SIZE);
}

static uacpi_size size_for_aml_pin_group(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_size source_length, label_length, vendor_length, size;

    size = aml_size_with_header(spec);
    switch (spec->type) {
    case UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION: {
        uacpi_resource_pin_group_function *res = &resource->pin_group_function;
        source_length = res->source.length;
        label_length = res->label.length;
        vendor_length = res->vendor_data_length;
        break;
    }

    case UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION: {
        uacpi_resource_pin_group_configuration *res;
        res = &resource->pin_group_configuration;
        source_length = res->source.length;
        label_length = res->label.length;
        vendor_length = res->vendor_data_length;
        break;
    }

    default:
        return 0;
    }

    size += source_length;
    size += label_length;
    size += vendor_length;

    return size;
}

#define AML_SERIAL_RESOURCE_EXTRA_SIZE(type)    \
    (sizeof(struct acpi_resource_serial_##type) \
     - sizeof(struct acpi_resource_serial))

#define NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(type) \
    (sizeof(uacpi_resource_##type##_connection)  \
     - sizeof(uacpi_resource_serial_bus_common))

static const uacpi_u8 aml_serial_resource_to_extra_aml_size
[ACPI_SERIAL_TYPE_MAX + 1] = {
    [ACPI_SERIAL_TYPE_I2C] = AML_SERIAL_RESOURCE_EXTRA_SIZE(i2c),
    [ACPI_SERIAL_TYPE_SPI] = AML_SERIAL_RESOURCE_EXTRA_SIZE(spi),
    [ACPI_SERIAL_TYPE_UART] = AML_SERIAL_RESOURCE_EXTRA_SIZE(uart),
    [ACPI_SERIAL_TYPE_CSI2] = AML_SERIAL_RESOURCE_EXTRA_SIZE(csi2),
};

static const uacpi_u8 aml_serial_resource_to_extra_native_size
[ACPI_SERIAL_TYPE_MAX + 1] = {
    [ACPI_SERIAL_TYPE_I2C] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(i2c),
    [ACPI_SERIAL_TYPE_SPI] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(spi),
    [ACPI_SERIAL_TYPE_UART] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(uart),
    [ACPI_SERIAL_TYPE_CSI2] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(csi2),
};

static uacpi_size extra_size_for_serial_connection(
    const struct uacpi_resource_spec *spec, void *data, uacpi_size size
)
{
    struct acpi_resource_serial *serial = data;
    uacpi_size extra_bytes = size;

    extra_bytes -= spec->aml_size;
    extra_bytes -= aml_serial_resource_to_extra_aml_size[serial->type];
    extra_bytes += aml_serial_resource_to_extra_native_size[serial->type];

    return extra_bytes;
}

static uacpi_size aml_size_for_serial_connection(
    const struct uacpi_resource_spec *spec, uacpi_resource *resource
)
{
    uacpi_size size;
    uacpi_resource_serial_bus_common *serial_bus = &resource->serial_bus_common;

    size = aml_size_with_header(spec);
    size += aml_serial_resource_to_extra_aml_size[serial_bus->type];
    size += serial_bus->vendor_data_length;
    size += serial_bus->source.length;

    return size;
}

#define OP(short_code, ...)                             \
{                                                       \
    .code = UACPI_RESOURCE_CONVERT_OPCODE_##short_code, \
    __VA_ARGS__                                         \
}

#define END() OP(END)

#define AML_O(short_aml_name, field) \
    uacpi_offsetof(struct acpi_resource_##short_aml_name, field)

#define AML_F(short_aml_name, field) \
    .f1.aml_offset = AML_O(short_aml_name, field)

#define NATIVE_O(short_name, field) \
    uacpi_offsetof(uacpi_resource_##short_name, field)

#define NATIVE_F(short_native_name, field) \
    .f2.native_offset = NATIVE_O(short_native_name, field)

#define IMM(value) .f3.imm = value
#define ARG0(value) .f1.arg0 = (value)
#define ARG1(value) .f2.arg1 = (value)
#define ARG2(value) .f3.arg2 = (value)


static const struct uacpi_resource_convert_instruction convert_irq_to_native[] = {
    OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
       ARG2(NATIVE_O(irq, num_irqs))),
    OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(3), IMM(6)),
        OP(SET_TO_IMM, NATIVE_F(irq, length_kind),
           IMM(UACPI_RESOURCE_LENGTH_KIND_FULL)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, triggering), IMM(0)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, polarity), IMM(3)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, sharing), IMM(4)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, wake_capability), IMM(5)),
        END(),
    OP(SET_TO_IMM, NATIVE_F(irq, length_kind),
       IMM(UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)),
    OP(SET_TO_IMM, NATIVE_F(irq, triggering), IMM(UACPI_TRIGGERING_EDGE)),
    END(),
};

const struct uacpi_resource_convert_instruction convert_irq_to_aml[] = {
    OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
       ARG2(NATIVE_O(irq, num_irqs))),
    OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(3), IMM(4)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, triggering), IMM(0)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, polarity), IMM(3)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, sharing), IMM(4)),
        OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, wake_capability), IMM(5)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_dma[] = {
    OP(PACKED_ARRAY_8, AML_F(dma, channel_mask), NATIVE_F(dma, channels),
       ARG2(NATIVE_O(dma, num_channels))),
    OP(BIT_FIELD_2, AML_F(dma, flags), NATIVE_F(dma, transfer_type), IMM(0)),
    OP(BIT_FIELD_1, AML_F(dma, flags), NATIVE_F(dma, bus_master_status), IMM(2)),
    OP(BIT_FIELD_2, AML_F(dma, flags), NATIVE_F(dma, channel_speed), IMM(5)),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_start_dependent_to_native[] = {
    OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(1), IMM(4)),
        OP(SET_TO_IMM, NATIVE_F(start_dependent, length_kind),
           IMM(UACPI_RESOURCE_LENGTH_KIND_FULL)),
        OP(BIT_FIELD_2, AML_F(start_dependent, flags),
           NATIVE_F(start_dependent, compatibility), IMM(0)),
        OP(BIT_FIELD_2, AML_F(start_dependent, flags),
           NATIVE_F(start_dependent, performance), IMM(2)),
        END(),
    OP(SET_TO_IMM, NATIVE_F(start_dependent, length_kind),
       IMM(UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)),
    OP(SET_TO_IMM, NATIVE_F(start_dependent, compatibility),
       IMM(UACPI_ACCEPTABLE)),
    OP(SET_TO_IMM, NATIVE_F(start_dependent, performance),
       IMM(UACPI_ACCEPTABLE)),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_start_dependent_to_aml[] = {
    OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(1), IMM(1)),
        OP(BIT_FIELD_2, AML_F(start_dependent, flags),
           NATIVE_F(start_dependent, compatibility), IMM(0)),
        OP(BIT_FIELD_2, AML_F(start_dependent, flags),
           NATIVE_F(start_dependent, performance), IMM(2)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_io[] = {
    OP(BIT_FIELD_1, AML_F(io, information), NATIVE_F(io, decode_type)),
    OP(FIELD_16, AML_F(io, minimum), NATIVE_F(io, minimum)),
    OP(FIELD_16, AML_F(io, maximum), NATIVE_F(io, maximum)),
    OP(FIELD_8, AML_F(io, alignment), NATIVE_F(io, alignment)),
    OP(FIELD_8, AML_F(io, length), NATIVE_F(io, length)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_fixed_io[] = {
    OP(FIELD_16, AML_F(fixed_io, address), NATIVE_F(fixed_io, address)),
    OP(FIELD_8, AML_F(fixed_io, length), NATIVE_F(fixed_io, length)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_fixed_dma[] = {
    OP(FIELD_16, AML_F(fixed_dma, request_line),
                 NATIVE_F(fixed_dma, request_line)),
    OP(FIELD_16, AML_F(fixed_dma, channel), NATIVE_F(fixed_dma, channel)),
    OP(FIELD_8, AML_F(fixed_dma, transfer_width),
                NATIVE_F(fixed_dma, transfer_width)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_vendor_type0[] = {
    OP(LOAD_AML_SIZE_32, NATIVE_F(vendor, length)),
    OP(FIELD_8, AML_F(vendor_defined_type0, byte_data), NATIVE_F(vendor, data)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_vendor_type1[] = {
    OP(LOAD_AML_SIZE_32, NATIVE_F(vendor, length)),
    OP(FIELD_8, AML_F(vendor_defined_type1, byte_data), NATIVE_F(vendor, data)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_memory24[] = {
    OP(BIT_FIELD_1, AML_F(memory24, information),
                    NATIVE_F(memory24, write_status), IMM(0)),
    OP(FIELD_16, AML_F(memory24, minimum), NATIVE_F(memory24, minimum), IMM(4)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_memory32[] = {
    OP(BIT_FIELD_1, AML_F(memory32, information),
                    NATIVE_F(memory32, write_status), IMM(0)),
    OP(FIELD_32, AML_F(memory32, minimum), NATIVE_F(memory32, minimum), IMM(4)),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_fixed_memory32[] = {
    OP(BIT_FIELD_1, AML_F(fixed_memory32, information),
                    NATIVE_F(fixed_memory32, write_status), IMM(0)),
    OP(FIELD_32, AML_F(fixed_memory32, address),
                 NATIVE_F(fixed_memory32, address)),
    OP(FIELD_32, AML_F(fixed_memory32, length),
                 NATIVE_F(fixed_memory32, length)),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_generic_register[] = {
    OP(FIELD_8, AML_F(generic_register, address_space_id),
                NATIVE_F(generic_register, address_space_id), IMM(4)),
    OP(FIELD_64, AML_F(generic_register, address),
                NATIVE_F(generic_register, address)),
    END(),
};

#define CONVERT_TYPE_SPECIFIC_FLAGS(addr_type)                                 \
    OP(LOAD_8_STORE, AML_F(addr_type, common.type),                            \
       NATIVE_F(addr_type, common.type)),                                      \
    OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_RANGE_MEMORY), IMM(5)),                  \
        OP(BIT_FIELD_1,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.memory.write_status), IMM(0)), \
        OP(BIT_FIELD_2,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.memory.caching), IMM(1)),      \
        OP(BIT_FIELD_2,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.memory.range_type), IMM(3)),   \
        OP(BIT_FIELD_1,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.memory.translation), IMM(5)),  \
        END(),                                                                 \
    OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_RANGE_IO), IMM(4)),                      \
        OP(BIT_FIELD_2,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.io.range_type), IMM(0)),       \
        OP(BIT_FIELD_1,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.io.translation_type), IMM(4)), \
        OP(BIT_FIELD_1,                                                        \
           AML_F(addr_type, common.type_flags),                                \
           NATIVE_F(addr_type, common.attribute.io.translation), IMM(5)),      \
        END(),                                                                 \
    /* Memory type that we don't know, just copy the byte */                   \
    OP(FIELD_8, AML_F(addr_type, common.type_flags),                           \
       NATIVE_F(addr_type, common.attribute.type_specific), IMM(0xFF)),        \
    END()

#define CONVERT_GENERAL_ADDRESS_FLAGS(addr_type)               \
    OP(BIT_FIELD_1,                                            \
       AML_F(addr_type, common.flags),                         \
       NATIVE_F(addr_type, common.direction), IMM(0)),         \
    OP(BIT_FIELD_1,                                            \
       AML_F(addr_type, common.flags),                         \
       NATIVE_F(addr_type, common.decode_type), IMM(1)),       \
    OP(BIT_FIELD_1,                                            \
       AML_F(addr_type, common.flags),                         \
       NATIVE_F(addr_type, common.fixed_min_address), IMM(2)), \
    OP(BIT_FIELD_1,                                            \
      AML_F(addr_type, common.flags),                          \
      NATIVE_F(addr_type, common.fixed_max_address), IMM(3))   \

#define DEFINE_ADDRESS_CONVERSION(width)                              \
    static const struct uacpi_resource_convert_instruction            \
    convert_address##width[] = {                                      \
        CONVERT_GENERAL_ADDRESS_FLAGS(address##width),                \
        OP(FIELD_##width, AML_F(address##width, granularity),         \
           NATIVE_F(address##width, granularity), IMM(5)),            \
        OP(RESOURCE_SOURCE, NATIVE_F(address##width, source)),        \
        CONVERT_TYPE_SPECIFIC_FLAGS(address##width),                  \
    };

DEFINE_ADDRESS_CONVERSION(16)
DEFINE_ADDRESS_CONVERSION(32)
DEFINE_ADDRESS_CONVERSION(64)

static const struct uacpi_resource_convert_instruction
convert_address64_extended[] = {
    CONVERT_GENERAL_ADDRESS_FLAGS(address64_extended),
    OP(FIELD_8, AML_F(address64_extended, revision_id),
       NATIVE_F(address64_extended, revision_id)),
    OP(FIELD_64, AML_F(address64_extended, granularity),
       NATIVE_F(address64_extended, granularity), IMM(6)),
    CONVERT_TYPE_SPECIFIC_FLAGS(address64_extended),
};

static const struct uacpi_resource_convert_instruction
convert_extended_irq[] = {
    OP(BIT_FIELD_1, AML_F(extended_irq, flags),
                    NATIVE_F(extended_irq, direction), IMM(0)),
    OP(BIT_FIELD_1, AML_F(extended_irq, flags),
                    NATIVE_F(extended_irq, triggering), IMM(1)),
    OP(BIT_FIELD_1, AML_F(extended_irq, flags),
                    NATIVE_F(extended_irq, polarity), IMM(2)),
    OP(BIT_FIELD_1, AML_F(extended_irq, flags),
                    NATIVE_F(extended_irq, sharing), IMM(3)),
    OP(BIT_FIELD_1, AML_F(extended_irq, flags),
                    NATIVE_F(extended_irq, wake_capability), IMM(4)),
    OP(LOAD_8_STORE, AML_F(extended_irq, num_irqs),
       NATIVE_F(extended_irq, num_irqs), IMM(4)),
    OP(RESOURCE_SOURCE, NATIVE_F(extended_irq, source)),

    // Use FIELD_8 here since the accumulator has been multiplied by 4
    OP(FIELD_8, AML_F(extended_irq, irqs), NATIVE_F(extended_irq, irqs)),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_clock_input[] = {
    OP(FIELD_8, AML_F(clock_input, revision_id),
       NATIVE_F(clock_input, revision_id)),
    OP(BIT_FIELD_1, AML_F(clock_input, flags), NATIVE_F(clock_input, frequency),
       IMM(0)),
    OP(BIT_FIELD_2, AML_F(clock_input, flags), NATIVE_F(clock_input, scale),
       IMM(1)),
    OP(FIELD_16, AML_F(clock_input, divisor), NATIVE_F(clock_input, divisor)),
    OP(FIELD_32, AML_F(clock_input, numerator), NATIVE_F(clock_input, numerator)),
    OP(FIELD_8, AML_F(clock_input, source_index), NATIVE_F(clock_input, source.index)),
    OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(clock_input, source)),
    END(),
};

#define DECODE_SOURCE_INDEX(short_aml_name)          \
    OP(FIELD_8, AML_F(short_aml_name, source_index), \
       NATIVE_F(short_aml_name, source.index))       \

#define DECODE_RES_PIN_TBL_AND_VENDOR_DATA(                            \
    short_aml_name, res_opcode, offset_field, res_field                \
)                                                                      \
    OP(LOAD_PIN_TABLE_LENGTH, AML_F(short_aml_name, offset_field),     \
       NATIVE_F(short_aml_name, pin_table_length)),                    \
    OP(RESOURCE_##res_opcode, NATIVE_F(short_aml_name, res_field),     \
       AML_F(short_aml_name, offset_field),                            \
       ARG2(AML_O(short_aml_name, vendor_data_offset))),               \
    OP(PIN_TABLE, AML_F(short_aml_name, pin_table_offset),             \
       NATIVE_F(short_aml_name, pin_table_length),                     \
       ARG2(NATIVE_O(short_aml_name, pin_table))),                     \
    OP(VENDOR_DATA, AML_F(short_aml_name, vendor_data_offset),         \
       NATIVE_F(short_aml_name, vendor_data_length),                   \
       ARG2(NATIVE_O(short_aml_name, vendor_data)))

static const struct uacpi_resource_convert_instruction
convert_gpio_connection[] = {
    OP(FIELD_8, AML_F(gpio_connection, revision_id),
       NATIVE_F(gpio_connection, revision_id)),
    OP(BIT_FIELD_1, AML_F(gpio_connection, general_flags),
       NATIVE_F(gpio_connection, direction)),
    OP(FIELD_8, AML_F(gpio_connection, pull_configuration),
       NATIVE_F(gpio_connection, pull_configuration)),
    OP(FIELD_16, AML_F(gpio_connection, drive_strength),
       NATIVE_F(gpio_connection, drive_strength), IMM(2)),
    DECODE_SOURCE_INDEX(gpio_connection),
    DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
        gpio_connection, SOURCE_NO_INDEX, source_offset, source
    ),
    OP(LOAD_8_STORE, AML_F(gpio_connection, type), NATIVE_F(gpio_connection, type)),
    OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_GPIO_CONNECTION_INTERRUPT), IMM(5)),
        OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
           NATIVE_F(gpio_connection, intr.triggering), IMM(0)),
        OP(BIT_FIELD_2, AML_F(gpio_connection, connection_flags),
           NATIVE_F(gpio_connection, intr.polarity), IMM(1)),
        OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
           NATIVE_F(gpio_connection, intr.sharing), IMM(3)),
        OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
           NATIVE_F(gpio_connection, intr.wake_capability), IMM(4)),
        END(),
    OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_GPIO_CONNECTION_IO), IMM(3)),
        OP(BIT_FIELD_2, AML_F(gpio_connection, connection_flags),
           NATIVE_F(gpio_connection, io.restriction), IMM(0)),
        OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
           NATIVE_F(gpio_connection, io.sharing), IMM(3)),
        END(),
    OP(FIELD_16, AML_F(gpio_connection, connection_flags),
       NATIVE_F(gpio_connection, type_specific), IMM(0xFF)),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_pin_function[] = {
    OP(FIELD_8, AML_F(pin_function, revision_id),
       NATIVE_F(pin_function, revision_id)),
    OP(BIT_FIELD_1, AML_F(pin_function, flags),
       NATIVE_F(pin_function, sharing), IMM(0)),
    OP(FIELD_8, AML_F(pin_function, pull_configuration),
       NATIVE_F(pin_function, pull_configuration)),
    OP(FIELD_16, AML_F(pin_function, function_number),
       NATIVE_F(pin_function, function_number)),
    DECODE_SOURCE_INDEX(pin_function),
    DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
        pin_function, SOURCE_NO_INDEX, source_offset, source
    ),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_pin_configuration[] = {
    OP(FIELD_8, AML_F(pin_configuration, revision_id),
       NATIVE_F(pin_configuration, revision_id)),
    OP(BIT_FIELD_1, AML_F(pin_configuration, flags),
       NATIVE_F(pin_configuration, sharing), IMM(0)),
    OP(BIT_FIELD_1, AML_F(pin_configuration, flags),
       NATIVE_F(pin_configuration, direction), IMM(1)),
    OP(FIELD_8, AML_F(pin_configuration, type),
       NATIVE_F(pin_configuration, type)),
    OP(FIELD_32, AML_F(pin_configuration, value),
       NATIVE_F(pin_configuration, value)),
    DECODE_SOURCE_INDEX(pin_configuration),
    DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
        pin_configuration, SOURCE_NO_INDEX, source_offset, source
    ),
    END(),
};

static const struct uacpi_resource_convert_instruction convert_pin_group[] = {
    OP(FIELD_8, AML_F(pin_group, revision_id),
       NATIVE_F(pin_group, revision_id)),
    OP(BIT_FIELD_1, AML_F(pin_group, flags),
       NATIVE_F(pin_group, direction), IMM(0)),
    DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
        pin_group, LABEL, source_lable_offset, label
    ),
    END(),
};

#define DECODE_PIN_GROUP_RES_SOURCES(postfix)                           \
    DECODE_SOURCE_INDEX(pin_group_##postfix),                           \
    OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(pin_group_##postfix, source), \
       AML_F(pin_group_##postfix, source_offset),                       \
       ARG2(AML_O(pin_group_##postfix, source_lable_offset))),          \
    OP(LOAD_16_NATIVE, NATIVE_F(pin_group_##postfix, source.length)),   \
    OP(RESOURCE_LABEL, NATIVE_F(pin_group_##postfix, label),            \
       AML_F(pin_group_##postfix, source_lable_offset),                 \
       ARG2(AML_O(pin_group_##postfix, vendor_data_offset))),           \
    OP(VENDOR_DATA, AML_F(pin_group_##postfix, vendor_data_offset),     \
       NATIVE_F(pin_group_##postfix, vendor_data_length),               \
       ARG2(NATIVE_O(pin_group_##postfix, vendor_data)))

static const struct uacpi_resource_convert_instruction
convert_pin_group_function[] = {
    OP(FIELD_8, AML_F(pin_group_function, revision_id),
       NATIVE_F(pin_group_function, revision_id)),
    OP(BIT_FIELD_1, AML_F(pin_group_function, flags),
       NATIVE_F(pin_group_function, sharing), IMM(0)),
    OP(BIT_FIELD_1, AML_F(pin_group_function, flags),
       NATIVE_F(pin_group_function, direction), IMM(1)),
    OP(FIELD_16, AML_F(pin_group_function, function),
       NATIVE_F(pin_group_function, function)),
    DECODE_PIN_GROUP_RES_SOURCES(function),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_pin_group_configuration[] = {
    OP(FIELD_8, AML_F(pin_group_configuration, revision_id),
       NATIVE_F(pin_group_configuration, revision_id)),
    OP(BIT_FIELD_1, AML_F(pin_group_configuration, flags),
       NATIVE_F(pin_group_configuration, sharing), IMM(0)),
    OP(BIT_FIELD_1, AML_F(pin_group_configuration, flags),
       NATIVE_F(pin_group_configuration, direction), IMM(1)),
    OP(FIELD_8, AML_F(pin_group_configuration, type),
       NATIVE_F(pin_group_configuration, type)),
    OP(FIELD_32, AML_F(pin_group_configuration, value),
       NATIVE_F(pin_group_configuration, value)),
    DECODE_PIN_GROUP_RES_SOURCES(configuration),
    END(),
};

static const struct uacpi_resource_convert_instruction
convert_generic_serial_bus[] = {
    OP(FIELD_8, AML_F(serial, revision_id),
       NATIVE_F(serial_bus_common, revision_id)),
    OP(FIELD_8, AML_F(serial, type_specific_revision_id),
       NATIVE_F(serial_bus_common, type_revision_id)),
    OP(FIELD_8, AML_F(serial, source_index),
       NATIVE_F(serial_bus_common, source.index)),
    OP(FIELD_16, AML_F(serial, type_data_length),
       NATIVE_F(serial_bus_common, type_data_length)),
    OP(BIT_FIELD_1, AML_F(serial, flags),
       NATIVE_F(serial_bus_common, mode), IMM(0)),
    OP(BIT_FIELD_1, AML_F(serial, flags),
       NATIVE_F(serial_bus_common, direction), IMM(1)),
    OP(BIT_FIELD_1, AML_F(serial, flags),
       NATIVE_F(serial_bus_common, sharing), IMM(2)),
    OP(SERIAL_TYPE_SPECIFIC, AML_F(serial, type),
       NATIVE_F(serial_bus_common, type)),
    OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(serial_bus_common, source)),
    OP(LOAD_8_NATIVE, NATIVE_F(serial_bus_common, type)),
    OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_I2C), IMM(4)),
        OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
           NATIVE_F(i2c_connection, addressing_mode), IMM(0)),
        OP(FIELD_32, AML_F(serial_i2c, connection_speed),
           NATIVE_F(i2c_connection, connection_speed), IMM(0xFF)),
        OP(FIELD_16, AML_F(serial_i2c, slave_address),
           NATIVE_F(i2c_connection, slave_address)),
        END(),
    OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_SPI), IMM(5)),
        OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
           NATIVE_F(spi_connection, wire_mode), IMM(0)),
        OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
           NATIVE_F(spi_connection, device_polarity), IMM(1)),
        OP(FIELD_32, AML_F(serial_spi, connection_speed),
           NATIVE_F(spi_connection, connection_speed), IMM(0xFF)),
        OP(FIELD_8, AML_F(serial_spi, data_bit_length),
           NATIVE_F(spi_connection, data_bit_length), IMM(5)),
        END(),
    OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_UART), IMM(8)),
        OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
           NATIVE_F(uart_connection, flow_control), IMM(0)),
        OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
           NATIVE_F(uart_connection, stop_bits), IMM(2)),
        OP(BIT_FIELD_3, AML_F(serial, type_specific_flags),
           NATIVE_F(uart_connection, data_bits), IMM(4)),
        OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
           NATIVE_F(uart_connection, endianness), IMM(7)),
        OP(FIELD_32, AML_F(serial_uart, baud_rate),
           NATIVE_F(uart_connection, baud_rate), IMM(0xFF)),
        OP(FIELD_16, AML_F(serial_uart, rx_fifo),
           NATIVE_F(uart_connection, rx_fifo), IMM(2)),
        OP(FIELD_8, AML_F(serial_uart, parity),
           NATIVE_F(uart_connection, parity), IMM(2)),
        END(),
    OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_CSI2), IMM(3)),
        OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
           NATIVE_F(csi2_connection, phy_type), IMM(0)),
        OP(BIT_FIELD_6, AML_F(serial, type_specific_flags),
           NATIVE_F(csi2_connection, local_port), IMM(2)),
        END(),

    /*
     * Insert a trap to catch unimplemented types, this should be unreachable
     * because of validation earlier.
     */
    OP(UNREACHABLE),
};

#define NATIVE_RESOURCE_HEADER_SIZE 8

#define DEFINE_SMALL_AML_RESOURCE(aml_type_enum, native_type_enum,           \
                                  aml_struct, native_struct, ...)            \
    [aml_type_enum] = {                                                      \
        .type = aml_type_enum,                                               \
        .native_type = native_type_enum,                                     \
        .resource_kind = UACPI_AML_RESOURCE_KIND_SMALL,                      \
        .aml_size = sizeof(aml_struct) - SMALL_ITEM_HEADER_SIZE,             \
        .native_size = sizeof(native_struct) + NATIVE_RESOURCE_HEADER_SIZE,  \
        __VA_ARGS__                                                          \
    }

#define DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(                \
    aml_type_enum, native_type_enum, aml_struct, ...             \
)                                                                \
    [aml_type_enum] = {                                          \
        .type = aml_type_enum,                                   \
        .native_type = native_type_enum,                         \
        .resource_kind = UACPI_AML_RESOURCE_KIND_SMALL,          \
        .aml_size = sizeof(aml_struct) - SMALL_ITEM_HEADER_SIZE, \
        .native_size = NATIVE_RESOURCE_HEADER_SIZE,              \
        __VA_ARGS__                                              \
    }

#define DEFINE_LARGE_AML_RESOURCE(aml_type_enum, native_type_enum,           \
                                  aml_struct, native_struct, ...)            \
    [aml_type_enum] = {                                                      \
        .type = aml_type_enum,                                               \
        .native_type = native_type_enum,                                     \
        .resource_kind = UACPI_AML_RESOURCE_KIND_LARGE,                      \
        .aml_size = sizeof(aml_struct) - LARGE_ITEM_HEADER_SIZE,             \
        .native_size = sizeof(native_struct) + NATIVE_RESOURCE_HEADER_SIZE,  \
        __VA_ARGS__                                                          \
    }

const struct uacpi_resource_spec aml_resources[UACPI_AML_RESOURCE_MAX + 1] = {
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_IRQ,
        UACPI_RESOURCE_TYPE_IRQ,
        struct acpi_resource_irq,
        uacpi_resource_irq,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
        .extra_size_for_native = extra_size_for_native_irq_or_dma,
        .size_for_aml = size_for_aml_irq,
        .to_native = convert_irq_to_native,
        .to_aml = convert_irq_to_aml,
    ),
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_DMA,
        UACPI_RESOURCE_TYPE_DMA,
        struct acpi_resource_dma,
        uacpi_resource_dma,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .extra_size_for_native = extra_size_for_native_irq_or_dma,
        .to_native = convert_dma,
        .to_aml = convert_dma,
    ),
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_START_DEPENDENT,
        UACPI_RESOURCE_TYPE_START_DEPENDENT,
        struct acpi_resource_start_dependent,
        uacpi_resource_start_dependent,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
        .size_for_aml = size_for_aml_start_dependent,
        .to_native = convert_start_dependent_to_native,
        .to_aml = convert_start_dependent_to_aml,
    ),
    DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(
        UACPI_AML_RESOURCE_END_DEPENDENT,
        UACPI_RESOURCE_TYPE_END_DEPENDENT,
        struct acpi_resource_end_dependent,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
    ),
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_IO,
        UACPI_RESOURCE_TYPE_IO,
        struct acpi_resource_io,
        uacpi_resource_io,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_io,
        .to_aml = convert_io,
    ),
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_FIXED_IO,
        UACPI_RESOURCE_TYPE_FIXED_IO,
        struct acpi_resource_fixed_io,
        uacpi_resource_fixed_io,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_fixed_io,
        .to_aml = convert_fixed_io,
    ),
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_FIXED_DMA,
        UACPI_RESOURCE_TYPE_FIXED_DMA,
        struct acpi_resource_fixed_dma,
        uacpi_resource_fixed_dma,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_fixed_dma,
        .to_aml = convert_fixed_dma,
    ),
    DEFINE_SMALL_AML_RESOURCE(
        UACPI_AML_RESOURCE_VENDOR_TYPE0,
        UACPI_RESOURCE_TYPE_VENDOR_SMALL,
        struct acpi_resource_vendor_defined_type0,
        uacpi_resource_vendor,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .size_for_aml = size_for_aml_vendor,
        .extra_size_for_native = extra_size_for_native_vendor,
        .to_native = convert_vendor_type0,
        .to_aml = convert_vendor_type0,
    ),
    DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(
        UACPI_AML_RESOURCE_END_TAG,
        UACPI_RESOURCE_TYPE_END_TAG,
        struct acpi_resource_end_tag,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_MEMORY24,
        UACPI_RESOURCE_TYPE_MEMORY24,
        struct acpi_resource_memory24,
        uacpi_resource_memory24,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_memory24,
        .to_aml = convert_memory24,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_GENERIC_REGISTER,
        UACPI_RESOURCE_TYPE_GENERIC_REGISTER,
        struct acpi_resource_generic_register,
        uacpi_resource_generic_register,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_generic_register,
        .to_aml = convert_generic_register,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_VENDOR_TYPE1,
        UACPI_RESOURCE_TYPE_VENDOR_LARGE,
        struct acpi_resource_vendor_defined_type1,
        uacpi_resource_vendor,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_vendor,
        .size_for_aml = size_for_aml_vendor,
        .to_native = convert_vendor_type1,
        .to_aml = convert_vendor_type1,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_MEMORY32,
        UACPI_RESOURCE_TYPE_MEMORY32,
        struct acpi_resource_memory32,
        uacpi_resource_memory32,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_memory32,
        .to_aml = convert_memory32,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_FIXED_MEMORY32,
        UACPI_RESOURCE_TYPE_FIXED_MEMORY32,
        struct acpi_resource_fixed_memory32,
        uacpi_resource_fixed_memory32,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_fixed_memory32,
        .to_aml = convert_fixed_memory32,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_ADDRESS32,
        UACPI_RESOURCE_TYPE_ADDRESS32,
        struct acpi_resource_address32,
        uacpi_resource_address32,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_address_or_clock_input,
        .size_for_aml = size_for_aml_address_or_clock_input,
        .to_native = convert_address32,
        .to_aml = convert_address32,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_ADDRESS16,
        UACPI_RESOURCE_TYPE_ADDRESS16,
        struct acpi_resource_address16,
        uacpi_resource_address16,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_address_or_clock_input,
        .size_for_aml = size_for_aml_address_or_clock_input,
        .to_native = convert_address16,
        .to_aml = convert_address16,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_EXTENDED_IRQ,
        UACPI_RESOURCE_TYPE_EXTENDED_IRQ,
        struct acpi_resource_extended_irq,
        uacpi_resource_extended_irq,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_extended_irq,
        .size_for_aml = size_for_aml_extended_irq,
        .to_native = convert_extended_irq,
        .to_aml = convert_extended_irq,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_ADDRESS64,
        UACPI_RESOURCE_TYPE_ADDRESS64,
        struct acpi_resource_address64,
        uacpi_resource_address64,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_address_or_clock_input,
        .size_for_aml = size_for_aml_address_or_clock_input,
        .to_native = convert_address64,
        .to_aml = convert_address64,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
        UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED,
        struct acpi_resource_address64_extended,
        uacpi_resource_address64_extended,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
        .to_native = convert_address64_extended,
        .to_aml = convert_address64_extended,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_GPIO_CONNECTION,
        UACPI_RESOURCE_TYPE_GPIO_CONNECTION,
        struct acpi_resource_gpio_connection,
        uacpi_resource_gpio_connection,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_gpio_or_pins,
        .size_for_aml = size_for_aml_gpio_or_pins,
        .to_aml = convert_gpio_connection,
        .to_native = convert_gpio_connection,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_PIN_FUNCTION,
        UACPI_RESOURCE_TYPE_PIN_FUNCTION,
        struct acpi_resource_pin_function,
        uacpi_resource_pin_function,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_gpio_or_pins,
        .size_for_aml = size_for_aml_gpio_or_pins,
        .to_aml = convert_pin_function,
        .to_native = convert_pin_function,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_SERIAL_CONNECTION,
        0, // the native type here is determined dynamically
        struct acpi_resource_serial,
        uacpi_resource_serial_bus_common,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_serial_connection,
        .size_for_aml = aml_size_for_serial_connection,
        .to_native = convert_generic_serial_bus,
        .to_aml = convert_generic_serial_bus,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_PIN_CONFIGURATION,
        UACPI_RESOURCE_TYPE_PIN_CONFIGURATION,
        struct acpi_resource_pin_configuration,
        uacpi_resource_pin_configuration,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_gpio_or_pins,
        .size_for_aml = size_for_aml_gpio_or_pins,
        .to_native = convert_pin_configuration,
        .to_aml = convert_pin_configuration,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_PIN_GROUP,
        UACPI_RESOURCE_TYPE_PIN_GROUP,
        struct acpi_resource_pin_group,
        uacpi_resource_pin_group,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_gpio_or_pins,
        .size_for_aml = size_for_aml_gpio_or_pins,
        .to_native = convert_pin_group,
        .to_aml = convert_pin_group,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
        UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION,
        struct acpi_resource_pin_group_function,
        uacpi_resource_pin_group_function,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_pin_group,
        .size_for_aml = size_for_aml_pin_group,
        .to_native = convert_pin_group_function,
        .to_aml = convert_pin_group_function,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
        UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION,
        struct acpi_resource_pin_group_configuration,
        uacpi_resource_pin_group_configuration,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_pin_group,
        .size_for_aml = size_for_aml_pin_group,
        .to_native = convert_pin_group_configuration,
        .to_aml = convert_pin_group_configuration,
    ),
    DEFINE_LARGE_AML_RESOURCE(
        UACPI_AML_RESOURCE_CLOCK_INPUT,
        UACPI_RESOURCE_TYPE_CLOCK_INPUT,
        struct acpi_resource_clock_input,
        uacpi_resource_clock_input,
        .size_kind =  UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
        .extra_size_for_native = extra_size_for_native_address_or_clock_input,
        .size_for_aml = size_for_aml_address_or_clock_input,
        .to_native = convert_clock_input,
        .to_aml = convert_clock_input,
    ),
};

static enum uacpi_aml_resource get_aml_resource_type(uacpi_u8 raw_byte)
{
    if (raw_byte & ACPI_LARGE_ITEM) {
        return aml_resource_to_type[
            LARGE_RESOURCE_BASE + (raw_byte & ACPI_LARGE_ITEM_NAME_MASK)
        ];
    }

    return aml_resource_to_type[
        (raw_byte >> ACPI_SMALL_ITEM_NAME_IDX) & ACPI_SMALL_ITEM_NAME_MASK
    ];
}

static uacpi_status get_aml_resource_size(
    uacpi_u8 *data, uacpi_size bytes_left, uacpi_u16 *out_size
)
{
    uacpi_u16 size;

    /*
     * Resource header is not included in size for both, so we subtract
     * the header size from bytes_left to validate it.
     */
    if (*data & ACPI_LARGE_ITEM) {
        if (uacpi_unlikely(bytes_left < 3))
            return UACPI_STATUS_AML_INVALID_RESOURCE;

        uacpi_memcpy(&size, data + 1, sizeof(size));
        bytes_left -= aml_resource_kind_to_header_size[
            UACPI_AML_RESOURCE_KIND_LARGE
        ];
    } else {
        size = *data & ACPI_SMALL_ITEM_LENGTH_MASK;
        bytes_left -= aml_resource_kind_to_header_size[
            UACPI_AML_RESOURCE_KIND_SMALL
        ];
    }

    if (uacpi_unlikely(size > bytes_left))
        return UACPI_STATUS_AML_INVALID_RESOURCE;

    *out_size = size;
    return UACPI_STATUS_OK;
}

static uacpi_status validate_aml_serial_type(uacpi_u8 type)
{
    if (uacpi_unlikely(type < ACPI_SERIAL_TYPE_I2C ||
                       type > ACPI_SERIAL_TYPE_CSI2)) {
        uacpi_error("invalid/unsupported serial connection type %d\n", type);
        return UACPI_STATUS_AML_INVALID_RESOURCE;
    }

    return UACPI_STATUS_OK;
}

uacpi_status uacpi_for_each_aml_resource(
    uacpi_data_view buffer, uacpi_aml_resource_iteration_callback cb, void *user
)
{
    uacpi_status ret;
    uacpi_iteration_decision decision;
    uacpi_u8 *data;
    uacpi_size bytes_left;
    uacpi_u16 resource_size;
    enum uacpi_aml_resource type;
    const struct uacpi_resource_spec *spec;

    bytes_left = buffer.length;
    data = buffer.bytes;

    while (bytes_left) {
        type = get_aml_resource_type(*data);
        if (uacpi_unlikely(type == UACPI_AML_RESOURCE_TYPE_INVALID))
            return UACPI_STATUS_AML_INVALID_RESOURCE;

        ret = get_aml_resource_size(data, bytes_left, &resource_size);
        if (uacpi_unlikely_error(ret))
            return ret;

        spec = &aml_resources[type];
        switch (spec->size_kind) {
        case UACPI_AML_RESOURCE_SIZE_KIND_FIXED:
            if (resource_size != spec->aml_size)
                return UACPI_STATUS_AML_INVALID_RESOURCE;
            break;
        case UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE:
            if (resource_size < spec->aml_size)
                return UACPI_STATUS_AML_INVALID_RESOURCE;
            break;
        case UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS:
            if (resource_size != spec->aml_size &&
                resource_size != (spec->aml_size - 1))
                return UACPI_STATUS_AML_INVALID_RESOURCE;
            break;
        default:
            return UACPI_STATUS_INTERNAL_ERROR;
        }

        if (spec->type == UACPI_AML_RESOURCE_SERIAL_CONNECTION) {
            struct acpi_resource_serial *serial;

            serial = (struct acpi_resource_serial*)data;

            ret = validate_aml_serial_type(serial->type);
            if (uacpi_unlikely_error(ret))
                return ret;
        }

        decision = cb(user, data, resource_size, spec);
        switch (decision) {
        case UACPI_ITERATION_DECISION_BREAK:
            return UACPI_STATUS_OK;
        case UACPI_ITERATION_DECISION_CONTINUE: {
            uacpi_size total_size = resource_size;

            total_size += aml_resource_kind_to_header_size[spec->resource_kind];
            data += total_size;
            bytes_left -= total_size;
            break;
        }
        default:
            return UACPI_STATUS_INTERNAL_ERROR;
        }

        if (type == UACPI_AML_RESOURCE_END_TAG)
            return UACPI_STATUS_OK;
    }

    return UACPI_STATUS_NO_RESOURCE_END_TAG;
}

static uacpi_iteration_decision find_end(
    void *opaque, uacpi_u8 *data, uacpi_u16 resource_size,
    const struct uacpi_resource_spec *spec
)
{
    uacpi_u8 **out_ptr = opaque;
    UACPI_UNUSED(resource_size);

    if (spec->type != UACPI_AML_RESOURCE_END_TAG)
        return UACPI_ITERATION_DECISION_CONTINUE;

    *out_ptr = data;
    return UACPI_ITERATION_DECISION_BREAK;
}

static uacpi_size native_size_for_aml_resource(
    uacpi_u8 *data, uacpi_u16 size, const struct uacpi_resource_spec *spec
)
{
    uacpi_size final_size = spec->native_size;

    if (spec->extra_size_for_native)
        final_size += spec->extra_size_for_native(spec, data, size);

    return UACPI_ALIGN_UP(final_size, sizeof(void*), uacpi_size);
}

uacpi_status uacpi_find_aml_resource_end_tag(
    uacpi_data_view buffer, uacpi_size *out_offset
)
{
    uacpi_u8 *end_tag_ptr = UACPI_NULL;
    uacpi_status ret;

    if (buffer.length == 0) {
        *out_offset = 0;
        return UACPI_STATUS_OK;
    }

    /*
     * This returning UACPI_STATUS_OK guarantees that end_tag_ptr is set to
     * a valid value because a missing end tag would produce a
     * UACPI_STATUS_NO_RESOURCE_END_TAG error.
     */
    ret = uacpi_for_each_aml_resource(buffer, find_end, &end_tag_ptr);
    if (uacpi_unlikely_error(ret))
        return ret;

    *out_offset = end_tag_ptr - buffer.bytes;
    return UACPI_STATUS_OK;
}

struct resource_conversion_ctx {
    union {
        void *buf;
        uacpi_u8 *byte_buf;
        uacpi_size size;
    };
    uacpi_status st;
    uacpi_bool just_one;
};

static uacpi_iteration_decision conditional_continue(
    struct resource_conversion_ctx *ctx
)
{
    return ctx->just_one ? UACPI_ITERATION_DECISION_BREAK :
                           UACPI_ITERATION_DECISION_CONTINUE;
}

// Opcodes that are the same for both AML->native and native->AML
#define CONVERSION_OPCODES_COMMON(native_buf)                                \
    case UACPI_RESOURCE_CONVERT_OPCODE_END:                                  \
        return conditional_continue(ctx);                                    \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8:                              \
    case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_16:                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_32:                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_64: {                           \
        uacpi_u8 bytes;                                                      \
                                                                             \
        bytes = 1 << (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8);   \
        accumulator = insn->f3.imm == 0xFF ? 0 : accumulator + insn->f3.imm; \
                                                                             \
        uacpi_memcpy(dst, src, bytes * UACPI_MAX(1, accumulator));           \
        accumulator = 0;                                                     \
        break;                                                               \
    }                                                                        \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_AML_SIZE_LESS_THAN:           \
        if (aml_size < insn->f1.arg0)                                        \
            pc += insn->f3.imm;                                              \
        break;                                                               \
    case UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_NOT_EQUALS:                   \
        if (insn->f1.arg0 != accumulator)                                    \
            pc += insn->f3.imm;                                              \
        break;                                                               \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_SET_TO_IMM:                           \
        uacpi_memcpy(dst, &insn->f3.imm, sizeof(insn->f3.imm));              \
        break;                                                               \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_IMM:                             \
        accumulator = insn->f3.imm;                                          \
        break;                                                               \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_STORE:                         \
        uacpi_memcpy_zerout(&accumulator, src, sizeof(accumulator), 1);      \
        uacpi_memcpy(dst, &accumulator, 1);                                  \
                                                                             \
        if (insn->f3.imm)                                                    \
            accumulator *= insn->f3.imm;                                     \
        break;                                                               \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE:                        \
    case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_16_NATIVE: {                     \
        uacpi_u8 bytes;                                                      \
                                                                             \
        bytes =                                                              \
            1 << (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE); \
        uacpi_memcpy_zerout(                                                 \
            &accumulator, native_buf, sizeof(accumulator), bytes             \
        );                                                                   \
        break;                                                               \
    }                                                                        \
                                                                             \
    case UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE:                          \
    default:                                                                 \
        if (insn->code != UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE) {       \
            uacpi_error("unhandled resource conversion opcode %d\n",         \
                       insn->code);                                          \
        } else {                                                             \
            uacpi_error("tried to execute unreachable conversion opcode\n"); \
        }                                                                    \
        ctx->st = UACPI_STATUS_INTERNAL_ERROR;                               \
        return UACPI_ITERATION_DECISION_BREAK;

#define PTR_AT(ptr, offset) (void*)((uacpi_u8*)(ptr) + (offset))

#define NATIVE_OFFSET(res, offset) \
    PTR_AT(res, (offset) + (sizeof(uacpi_u32) * 2))

#define NATIVE_FIELD(res, name, field) \
    NATIVE_OFFSET(res, NATIVE_O(name, field))

#define CHECK_AML_OOB(offset, prefix, what)                                  \
    if (uacpi_unlikely(offset > ((uacpi_u32)aml_size + header_size))) {      \
        uacpi_error(prefix what " is OOB: %zu > %u\n",                       \
                    (uacpi_size)offset, (uacpi_u32)aml_size + header_size);  \
        ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;                         \
        return UACPI_ITERATION_DECISION_BREAK;                               \
    }

#define CHECK_AML_OFFSET_BASE(offset, what)                             \
    if (uacpi_unlikely(offset < base_aml_size_with_header)) {           \
        uacpi_error(                                                    \
            "invalid " what " offset: %zu, expected at least %u\n",     \
            (uacpi_size)offset, base_aml_size_with_header);             \
        ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;                    \
        return UACPI_ITERATION_DECISION_BREAK;                          \
    }

#define CHECK_AML_OFFSET(offset, what)     \
    CHECK_AML_OOB(offset, "end of ", what) \
    CHECK_AML_OFFSET_BASE(offset, what)

static uacpi_resource_type aml_serial_to_native_type(
        uacpi_u8 type
)
{
    return (type - ACPI_SERIAL_TYPE_I2C) +
           UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION;
}

static uacpi_iteration_decision do_aml_resource_to_native(
    void *opaque, uacpi_u8 *data, uacpi_u16 aml_size,
    const struct uacpi_resource_spec *spec
)
{
    struct resource_conversion_ctx *ctx = opaque;
    uacpi_resource *resource = ctx->buf;
    const struct uacpi_resource_convert_instruction *insns, *insn;
    uacpi_u8 header_size, pc = 0;
    uacpi_u8 *src, *dst;
    void *resource_end;
    uacpi_u16 base_aml_size;
    uacpi_u32 base_aml_size_with_header, accumulator = 0;

    insns = spec->to_native;

    header_size = aml_resource_kind_to_header_size[spec->resource_kind];
    resource->type = spec->native_type;
    resource->length = native_size_for_aml_resource(data, aml_size, spec);
    resource_end = ctx->byte_buf + spec->native_size;
    ctx->byte_buf += resource->length;

    base_aml_size = base_aml_size_with_header = spec->aml_size;
    base_aml_size_with_header += header_size;

    if (insns == UACPI_NULL)
        return conditional_continue(ctx);

    for (;;) {
        insn = &insns[pc++];

        src = data + insn->f1.aml_offset;
        dst = NATIVE_OFFSET(resource, insn->f2.native_offset);

        switch (insn->code) {
        case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8:
        case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16: {
            uacpi_size i, j, max_bit;
            uacpi_u16 value;

            if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16) {
                max_bit = 16;
                uacpi_memcpy(&value, src, sizeof(uacpi_u16));
            } else {
                max_bit = 8;
                uacpi_memcpy_zerout(
                    &value, src, sizeof(value), sizeof(uacpi_u8)
                );
            }

            for (i = 0, j = 0; i < max_bit; ++i) {
                if (!(value & (1 << i)))
                    continue;

                dst[j++] = i;
            }

            uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2), &j, 1);
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1:
        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2:
        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3:
        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6:{
            uacpi_u8 mask, value;

            mask = (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1) + 1;
            mask = (1 << mask) - 1;

            value = (*src >> insn->f3.imm) & mask;
            uacpi_memcpy(dst, &value, sizeof(value));
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32:
            accumulator = aml_size;
            uacpi_memcpy(dst, &accumulator, 4);
            break;

        case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE:
        case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX:
        case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL: {
            uacpi_size offset = 0, max_offset, length = 0;
            uacpi_char *src_string, *dst_string;
            union {
                void *ptr;
                uacpi_resource_source *source;
                uacpi_resource_label *label;
            } dst_name = { 0 };

            dst_name.ptr = dst;

            /*
             * Check if the string is bounded by anything at the top. If not, we
             * just assume it ends at the end of the resource.
             */
            if (insn->f3.arg2) {
                uacpi_memcpy_zerout(&max_offset, data + insn->f3.arg2,
                                    sizeof(max_offset), sizeof(uacpi_u16));
                CHECK_AML_OFFSET(max_offset, "resource source");
            } else {
                max_offset = aml_size + header_size;
            }

            offset += base_aml_size_with_header;
            offset += accumulator;

            if (insn->code != UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL)
                dst_name.source->index_present = UACPI_TRUE;

            if (offset >= max_offset) {
                if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE)
                    dst_name.source->index_present = UACPI_FALSE;
                break;
            }

            src_string = PTR_AT(data, offset);

            if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE) {
                uacpi_memcpy(&dst_name.source->index, src_string++, 1);
                offset++;
            }

            if (offset == max_offset)
                break;

            while (offset++ < max_offset) {
                if (src_string[length++] == '\0')
                    break;
            }

            if (src_string[length - 1] != '\0') {
                uacpi_error("non-null-terminated resource source string\n");
                ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
                return UACPI_ITERATION_DECISION_BREAK;
            }

            dst_string = PTR_AT(resource_end, accumulator);
            uacpi_memcpy(dst_string, src_string, length);

            if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL) {
                dst_name.label->length = length;
                dst_name.label->string = dst_string;
            } else {
                dst_name.source->length = length;
                dst_name.source->string = dst_string;
            }

            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH:
            uacpi_memcpy_zerout(&accumulator, src,
                                sizeof(accumulator), sizeof(uacpi_u16));
            CHECK_AML_OFFSET(accumulator, "pin table");

            accumulator -= base_aml_size_with_header;
            break;

        case UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE: {
            uacpi_u16 entry_count = accumulator / 2;

            /*
             * Pin table is stored right at the end of the resource buffer,
             * copy the data there.
             */
            uacpi_memcpy(
                resource_end,
                data + base_aml_size_with_header,
                accumulator
            );

            // Set pin_table_length
            uacpi_memcpy(dst, &entry_count, sizeof(entry_count));

            // Set pin_table pointer
            uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2),
                         &resource_end, sizeof(void*));
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA: {
            uacpi_size length;
            uacpi_u16 data_offset, offset_from_end;
            void *native_dst, *vendor_data;

            uacpi_memcpy(&data_offset, src, sizeof(data_offset));
            CHECK_AML_OFFSET(data_offset, "vendor data");

            vendor_data = data + data_offset;

            /*
             * Rebase the offset to cut off the header as it's not included
             * in the size fields.
             */
            data_offset -= header_size;

            length = aml_size - data_offset;
            if (length == 0)
                break;

            uacpi_memcpy(dst, &length, sizeof(uacpi_u16));

            offset_from_end = data_offset - base_aml_size;
            native_dst = PTR_AT(resource_end, offset_from_end);

            uacpi_memcpy(native_dst, vendor_data, length);
            uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2),
                         &native_dst, sizeof(void*));
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC: {
            uacpi_resource_serial_bus_common *serial_bus_common;
            uacpi_u8 serial_type, extra_size, type_length;

            serial_bus_common = &resource->serial_bus_common;
            serial_type = *src;
            serial_bus_common->type = serial_type;
            resource->type = aml_serial_to_native_type(serial_type);

            /*
             * Now that we know the serial type rebase the end pointers and
             * sizes.
             */
            resource_end = PTR_AT(
                resource_end,
                aml_serial_resource_to_extra_native_size[serial_type]
            );
            extra_size = aml_serial_resource_to_extra_aml_size[serial_type];
            base_aml_size += extra_size;
            base_aml_size_with_header += extra_size;

            type_length = serial_bus_common->type_data_length;
            if (uacpi_unlikely(type_length < extra_size)) {
                uacpi_error(
                    "invalid type-specific data length: %d, "
                    "expected at least %d\n", type_length, extra_size
                );
                ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
                return UACPI_ITERATION_DECISION_BREAK;
            }

            /*
             * Calculate the length of the vendor data. All the extra data
             * beyond the end of type-specific size is considered vendor data.
             */
            accumulator = type_length - extra_size;
            if (accumulator == 0)
                break;

            serial_bus_common->vendor_data_length = accumulator;
            serial_bus_common->vendor_data = resource_end;
            uacpi_memcpy(
                resource_end,
                data + base_aml_size_with_header,
                accumulator
            );
            break;
        }

        CONVERSION_OPCODES_COMMON(dst)
        }
    }
}

static uacpi_iteration_decision accumulate_native_buffer_size(
    void *opaque, uacpi_u8 *data, uacpi_u16 resource_size,
    const struct uacpi_resource_spec *spec
)
{
    struct resource_conversion_ctx *ctx = opaque;
    uacpi_size size_for_this;

    size_for_this = native_size_for_aml_resource(data, resource_size, spec);
    if (size_for_this == 0 || (ctx->size + size_for_this) < ctx->size) {
        uacpi_error("invalid native size for aml resource: %zu\n",
                    size_for_this);
        ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
        return UACPI_ITERATION_DECISION_BREAK;
    }

    ctx->size += size_for_this;
    return conditional_continue(ctx);
}

static uacpi_status eval_resource_helper(
    uacpi_namespace_node *node, const uacpi_char *method,
    uacpi_object **out_obj
)
{
    uacpi_status ret;
    uacpi_bool is_device;

    ret = uacpi_namespace_node_is(node, UACPI_OBJECT_DEVICE, &is_device);
    if (uacpi_unlikely_error(ret))
        return ret;

    if (uacpi_unlikely(!is_device))
        return UACPI_STATUS_INVALID_ARGUMENT;

    return uacpi_eval_simple_buffer(
        node, method, out_obj
    );
}

uacpi_status uacpi_native_resources_from_aml(
    uacpi_data_view aml_buffer, uacpi_resources **out_resources
)
{
    uacpi_status ret;
    struct resource_conversion_ctx ctx = { 0 };
    uacpi_resources *resources;

    ret = uacpi_for_each_aml_resource(
        aml_buffer, accumulate_native_buffer_size, &ctx
    );
    if (uacpi_unlikely_error(ret))
        return ret;

    if (uacpi_unlikely_error(ctx.st))
        return ctx.st;

    // Realistically any resource buffer bigger than this is probably a bug
    if (uacpi_unlikely(ctx.size > (5 * 1024u * 1024u))) {
        uacpi_error("bug: bogus native resource buffer size %zu\n", ctx.size);
        return UACPI_STATUS_INTERNAL_ERROR;
    }

    resources = uacpi_kernel_alloc_zeroed(ctx.size + sizeof(uacpi_resources));
    if (uacpi_unlikely(resources == UACPI_NULL))
        return UACPI_STATUS_OUT_OF_MEMORY;
    resources->length = ctx.size;
    resources->entries = UACPI_PTR_ADD(resources, sizeof(uacpi_resources));

    uacpi_memzero(&ctx, sizeof(ctx));
    ctx.buf = resources->entries;

    ret = uacpi_for_each_aml_resource(aml_buffer, do_aml_resource_to_native, &ctx);
    if (uacpi_unlikely_error(ret)) {
        uacpi_free_resources(resources);
        return ret;
    }

    *out_resources = resources;
    return ret;
}

uacpi_status uacpi_get_resource_from_buffer(
    uacpi_data_view aml_buffer, uacpi_resource **out_resource
)
{
    uacpi_status ret;
    struct resource_conversion_ctx ctx = {
        .just_one = UACPI_TRUE,
    };
    uacpi_resource *resource;

    ret = uacpi_for_each_aml_resource(
        aml_buffer, accumulate_native_buffer_size, &ctx
    );
    if (uacpi_unlikely_error(ret))
        return ret;

    resource = uacpi_kernel_alloc_zeroed(ctx.size);
    if (uacpi_unlikely(resource == UACPI_NULL))
        return UACPI_STATUS_OUT_OF_MEMORY;

    uacpi_memzero(&ctx, sizeof(ctx));
    ctx.buf = resource;
    ctx.just_one = UACPI_TRUE;

    ret = uacpi_for_each_aml_resource(aml_buffer, do_aml_resource_to_native, &ctx);
    if (uacpi_unlikely_error(ret)) {
        uacpi_free_resource(resource);
        return ret;
    }

    *out_resource = resource;
    return ret;
}

void uacpi_free_resources(uacpi_resources *resources)
{
    if (resources == UACPI_NULL)
        return;

    uacpi_free(resources, sizeof(uacpi_resources) + resources->length);
}

void uacpi_free_resource(uacpi_resource *resource)
{
    if (resource == UACPI_NULL)
        return;

    uacpi_free(resource, resource->length);
}

static uacpi_status extract_native_resources_from_method(
    uacpi_namespace_node *device, const uacpi_char *method,
    uacpi_resources **out_resources
)
{
    uacpi_status ret;
    uacpi_object *obj;
    uacpi_data_view buffer;

    ret = eval_resource_helper(device, method, &obj);
    if (uacpi_unlikely_error(ret))
        return ret;

    uacpi_buffer_to_view(obj->buffer, &buffer);

    ret = uacpi_native_resources_from_aml(buffer, out_resources);
    uacpi_object_unref(obj);

    return ret;
}

uacpi_status uacpi_get_current_resources(
    uacpi_namespace_node *device, uacpi_resources **out_resources
)
{
    return extract_native_resources_from_method(device, "_CRS", out_resources);
}

uacpi_status uacpi_get_possible_resources(
    uacpi_namespace_node *device, uacpi_resources **out_resources
)
{
    return extract_native_resources_from_method(device, "_PRS", out_resources);
}

uacpi_status uacpi_get_device_resources(
    uacpi_namespace_node *device, const uacpi_char *method,
    uacpi_resources **out_resources
)
{
    return extract_native_resources_from_method(device, method, out_resources);
}

uacpi_status uacpi_for_each_resource(
    uacpi_resources *resources, uacpi_resource_iteration_callback cb, void *user
)
{
    uacpi_size bytes_left = resources->length;
    uacpi_resource *current = resources->entries;
    uacpi_iteration_decision decision;

    while (bytes_left) {
        // At least the head bytes
        if (uacpi_unlikely(bytes_left < 4)) {
            uacpi_error("corrupted resource buffer %p length %zu\n",
                        resources, resources->length);
            return UACPI_STATUS_INVALID_ARGUMENT;
        }

        if (uacpi_unlikely(current->type > UACPI_RESOURCE_TYPE_MAX)) {
            uacpi_error("invalid resource type %d\n", current->type);
            return UACPI_STATUS_INVALID_ARGUMENT;
        }

        if (uacpi_unlikely(current->length > bytes_left)) {
            uacpi_error("corrupted resource@%p length %u (%zu bytes left)\n",
                        current, current->length, bytes_left);
            return UACPI_STATUS_INVALID_ARGUMENT;
        }

        decision = cb(user, current);

        if (decision == UACPI_ITERATION_DECISION_BREAK ||
            current->type == UACPI_RESOURCE_TYPE_END_TAG)
            return UACPI_STATUS_OK;

        bytes_left -= current->length;
        current = (uacpi_resource*)((uacpi_u8*)current + current->length);
    }

    return UACPI_STATUS_NO_RESOURCE_END_TAG;
}

uacpi_status uacpi_for_each_device_resource(
    uacpi_namespace_node *device, const uacpi_char *method,
    uacpi_resource_iteration_callback cb, void *user
)
{
    uacpi_status ret;
    uacpi_resources *resources;

    ret = extract_native_resources_from_method(device, method, &resources);
    if (uacpi_unlikely_error(ret))
        return ret;

    ret = uacpi_for_each_resource(resources, cb, user);
    uacpi_free_resources(resources);

    return ret;
}

static const struct uacpi_resource_spec *resource_spec_from_native(
        uacpi_resource *resource
)
{
    return &aml_resources[native_resource_to_type[resource->type]];
}

static uacpi_size aml_size_for_native_resource(
        uacpi_resource *resource, const struct uacpi_resource_spec *spec
)
{
    return spec->size_for_aml ?
           spec->size_for_aml(spec, resource) :
           aml_size_with_header(spec);
}

static uacpi_iteration_decision do_native_resource_to_aml(
    void *opaque, uacpi_resource *resource
)
{
    struct resource_conversion_ctx *ctx = opaque;
    const struct uacpi_resource_spec *spec;
    const struct uacpi_resource_convert_instruction *insns, *insn;
    uacpi_u8 pc = 0;
    uacpi_u8 *dst_base, *src, *dst;
    uacpi_u32 aml_size, base_aml_size_with_header, accumulator = 0;
    void *resource_end;

    spec = resource_spec_from_native(resource);
    aml_size = aml_size_for_native_resource(resource, spec);
    insns = spec->to_aml;

    dst_base = ctx->byte_buf;
    ctx->byte_buf += aml_size;
    aml_size -= aml_resource_kind_to_header_size[spec->resource_kind];

    base_aml_size_with_header = spec->aml_size;
    base_aml_size_with_header += aml_resource_kind_to_header_size[
        spec->resource_kind
    ];
    resource_end = PTR_AT(resource, spec->native_size);

    if (spec->resource_kind == UACPI_AML_RESOURCE_KIND_LARGE) {
        *dst_base = ACPI_LARGE_ITEM | type_to_aml_resource[spec->type];
        uacpi_memcpy(dst_base + 1, &aml_size, sizeof(uacpi_u16));
    } else {
        *dst_base = type_to_aml_resource[spec->type] << ACPI_SMALL_ITEM_NAME_IDX;
        *dst_base |= aml_size;
    }

    if (insns == UACPI_NULL)
        return UACPI_ITERATION_DECISION_CONTINUE;

    for (;;) {
        insn = &insns[pc++];

        src = NATIVE_OFFSET(resource, insn->f2.native_offset);
        dst = dst_base + insn->f1.aml_offset;

        switch (insn->code) {
        case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8:
        case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16: {
            uacpi_u8 i, *array_size, bytes = 1;
            uacpi_u16 mask = 0;

            array_size = NATIVE_OFFSET(resource, insn->f3.arg2);
            for (i = 0; i < *array_size; ++i)
                mask |= 1 << src[i];

            if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16)
                bytes = 2;

            uacpi_memcpy(dst, &mask, bytes);
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1:
        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2:
        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3:
        case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6:
            *dst |= *src << insn->f3.imm;
            break;

        case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32:
            accumulator = aml_size;
            break;

        case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE:
        case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX:
        case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL: {
            uacpi_size source_offset, length;
            uacpi_u8 *dst_string;
            const uacpi_char *src_string;
            union {
                void *ptr;
                uacpi_resource_source *source;
                uacpi_resource_label *label;
            } src_name = { 0 };

            src_name.ptr = src;

            source_offset = base_aml_size_with_header + accumulator;
            dst_string = dst_base + source_offset;

            if (insn->f1.aml_offset)
                uacpi_memcpy(dst, &source_offset, sizeof(uacpi_u16));

            if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE &&
                src_name.source->index_present)
                uacpi_memcpy(dst_string++, &src_name.source->index, 1);

            if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL) {
                length = src_name.label->length;
                src_string = src_name.label->string;
            } else {
                length = src_name.source->length;
                src_string = src_name.source->string;
            }

            if (length == 0)
                break;

            if (uacpi_unlikely(src_string == UACPI_NULL)) {
                uacpi_error(
                    "source string length is %zu but the pointer is NULL\n",
                    length
                );
                ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
                return UACPI_ITERATION_DECISION_BREAK;
            }

            uacpi_memcpy(dst_string, src_string, length);
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH:
            uacpi_memcpy_zerout(&accumulator, src,
                                sizeof(accumulator), sizeof(uacpi_u16));
            accumulator *= sizeof(uacpi_u16);
            break;

        case UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE:
            /*
             * The pin table resides right at the end of the base resource,
             * set the offset to it in the AML we're encoding.
             */
            uacpi_memcpy(dst, &base_aml_size_with_header, sizeof(uacpi_u16));

            /*
             * Copy the actual data. It also resides right at the end of the
             * native base resource.
             */
            uacpi_memcpy(
                dst_base + base_aml_size_with_header,
                resource_end,
                accumulator
            );
            break;

        case UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA: {
            uacpi_u16 vendor_data_length, data_offset, vendor_data_offset;
            uacpi_u8 *vendor_data;

            // Read the vendor_data pointer
            uacpi_memcpy(&vendor_data, NATIVE_OFFSET(resource, insn->f3.arg2),
                         sizeof(void*));
            uacpi_memcpy(&vendor_data_length, src, sizeof(uacpi_u16));

            if (vendor_data == UACPI_NULL) {
                uacpi_size full_aml_size;

                if (uacpi_unlikely(vendor_data_length != 0)) {
                    uacpi_error(
                        "vendor_data_length is %d, but pointer is NULL\n",
                        vendor_data_length
                    );
                    ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
                    return UACPI_ITERATION_DECISION_BREAK;
                }

                /*
                 * There's no vendor data. The specification still mandates
                 * that we fill the vendor data offset field correctly, meaning
                 * we set it to the total length of the resource.
                 */
                full_aml_size = aml_size;
                full_aml_size += aml_resource_kind_to_header_size[
                    spec->resource_kind
                ];

                uacpi_memcpy(dst, &full_aml_size, sizeof(uacpi_u16));
                break;
            }

            /*
             * Calculate the offset of vendor data from the end of the native
             * resource and use it since it matches the offset from the end of
             * the AML resource.
             *
             * Non-zero value means there's a source string in between.
             */
            data_offset = vendor_data - (uacpi_u8*)resource_end;
            vendor_data_offset = data_offset + base_aml_size_with_header;

            // Write vendor_data_offset
            uacpi_memcpy(dst, &vendor_data_offset, sizeof(uacpi_u16));

            /*
             * Write vendor_data_length, this field is right after
             * vendor_data_offset, and is completely redundant, but it exists
             * nonetheless.
             */
            uacpi_memcpy(
                dst + sizeof(uacpi_u16),
                &vendor_data_length,
                sizeof(vendor_data_length)
            );

            // Finally write the data itself
            uacpi_memcpy(
                dst_base + vendor_data_offset,
                vendor_data,
                vendor_data_length
            );
            break;
        }

        case UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC: {
            uacpi_u8 serial_type = *src;
            *dst = serial_type;

            ctx->st = validate_aml_serial_type(serial_type);
            if (uacpi_unlikely_error(ctx->st))
                return UACPI_ITERATION_DECISION_BREAK;

            if (uacpi_unlikely(resource->type !=
                               aml_serial_to_native_type(serial_type))) {
                uacpi_error(
                    "native serial resource type %d doesn't match expected %d\n",
                    resource->type, aml_serial_to_native_type(serial_type)
                );
                ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
                return UACPI_ITERATION_DECISION_BREAK;
            }

            // Rebase the end pointer & size now that we know the serial type
            resource_end = PTR_AT(
                resource_end,
                aml_serial_resource_to_extra_native_size[serial_type]
            );
            base_aml_size_with_header += aml_serial_resource_to_extra_aml_size[
                serial_type
            ];

            accumulator = resource->serial_bus_common.vendor_data_length;
            if (accumulator == 0)
                break;

            // Copy vendor data
            uacpi_memcpy(
                dst_base + base_aml_size_with_header,
                resource_end,
                accumulator
            );
            break;
        }

        CONVERSION_OPCODES_COMMON(src)
        }
    }
}

static uacpi_status native_resources_to_aml(
    uacpi_resources *native_resources, void *aml_buffer
)
{
    uacpi_status ret;
    struct resource_conversion_ctx ctx = { 0 };

    ctx.buf = aml_buffer;

    ret = uacpi_for_each_resource(
        native_resources, do_native_resource_to_aml, &ctx
    );
    if (ret == UACPI_STATUS_NO_RESOURCE_END_TAG) {
        // An end tag is always included
        uacpi_resource end_tag = { .type = UACPI_RESOURCE_TYPE_END_TAG };

        do_native_resource_to_aml(&ctx, &end_tag);
        ret = UACPI_STATUS_OK;
    }
    if (uacpi_unlikely_error(ret))
        return ret;

    return ctx.st;
}

static uacpi_iteration_decision accumulate_aml_buffer_size(
    void *opaque, uacpi_resource *resource
)
{
    struct resource_conversion_ctx *ctx = opaque;
    const struct uacpi_resource_spec *spec;
    uacpi_size size_for_this;

    // resource->type is sanitized to be valid here by the iteration function
    spec = resource_spec_from_native(resource);

    size_for_this = aml_size_for_native_resource(resource, spec);
    if (size_for_this == 0 || (ctx->size + size_for_this) < ctx->size) {
        uacpi_error("invalid aml size for native resource: %zu\n",
                    size_for_this);
        ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
        return UACPI_ITERATION_DECISION_BREAK;
    }

    ctx->size += size_for_this;
    return UACPI_ITERATION_DECISION_CONTINUE;
}

uacpi_status uacpi_native_resources_to_aml(
    uacpi_resources *resources, uacpi_object **out_template
)
{
    uacpi_status ret;
    uacpi_object *obj;
    void *buffer;
    struct resource_conversion_ctx ctx = { 0 };

    ret = uacpi_for_each_resource(
        resources, accumulate_aml_buffer_size, &ctx
    );
    if (ret == UACPI_STATUS_NO_RESOURCE_END_TAG) {
        // An end tag is always included
        uacpi_resource end_tag = { .type = UACPI_RESOURCE_TYPE_END_TAG };

        accumulate_aml_buffer_size(&ctx, &end_tag);
        ret = UACPI_STATUS_OK;
    }
    if (uacpi_unlikely_error(ret))
        return ret;
    if (uacpi_unlikely_error(ctx.st))
        return ctx.st;

    // Same reasoning as native_resource_from_aml
    if (uacpi_unlikely(ctx.size > (5 * 1024u * 1024u))) {
        uacpi_error("bug: bogus target aml resource buffer size %zu\n",
                    ctx.size);
        return UACPI_STATUS_INTERNAL_ERROR;
    }

    buffer = uacpi_kernel_alloc_zeroed(ctx.size);
    if (uacpi_unlikely(buffer == UACPI_NULL))
        return UACPI_STATUS_OUT_OF_MEMORY;

    obj = uacpi_create_object(UACPI_OBJECT_BUFFER);
    if (uacpi_unlikely(obj == UACPI_NULL)) {
        uacpi_free(buffer, ctx.size);
        return UACPI_STATUS_OUT_OF_MEMORY;
    }

    obj->buffer->data = buffer;
    obj->buffer->size = ctx.size;

    ret = native_resources_to_aml(resources, buffer);
    if (uacpi_unlikely_error(ret))
        uacpi_object_unref(obj);

    if (ret == UACPI_STATUS_OK)
        *out_template = obj;

    return ret;
}

uacpi_status uacpi_set_resources(
    uacpi_namespace_node *device, uacpi_resources *resources
)
{
    uacpi_status ret;
    uacpi_object *res_template;
    uacpi_object_array args;

    ret = uacpi_native_resources_to_aml(resources, &res_template);
    if (uacpi_unlikely_error(ret))
        return ret;

    args.objects = &res_template;
    args.count = 1;
    ret = uacpi_eval(device, "_SRS", &args, UACPI_NULL);

    uacpi_object_unref(res_template);
    return ret;
}

#endif // !UACPI_BAREBONES_MODE
