/*
 *     Copyright (c) 2025, NVIDIA CORPORATION.  All rights reserved.
 *
 * NVIDIA CORPORATION and its licensors retain all intellectual property
 * and proprietary rights in and to this software, related documentation
 * and any modifications thereto.  Any use, reproduction, disclosure or
 * distribution of this software and related documentation without an express
 * license agreement from NVIDIA CORPORATION is strictly prohibited.
 *
 */

/* Device versions of the C++ runtime __cxa_vec_* routines (only needed for CUDA 11.x). */

__ATTRIBUTES void
__cxa_vec_ctor(void *array_address, size_t element_count, size_t element_size,
               void (*constructor)(void *), void (*destructor)(void *))
{
    if (constructor) {
        char *element = (char *)array_address;
        char *end = element + element_count * element_size;
        for (; element < end; element += element_size) {
            constructor((void *)element);
        }
    }
}

__ATTRIBUTES void
__cxa_vec_cctor(void *destination_array, void *source_array,
                size_t element_count, size_t element_size,
                void (*constructor)(void *, void *), void (*destructor)(void *))
{
    if (constructor) {
        char *source = (char *)source_array;
        char *destination = (char *)destination_array;
        char *source_end = source + element_count * element_size;
        for (; source < source_end;
             source += element_size, destination += element_size) {
            constructor((void *)destination, (void *)source);
        }
    }
}

__ATTRIBUTES void
__cxa_vec_dtor(void *array_address, size_t element_count, size_t element_size,
               void (*destructor)(void *))
{
    if (destructor) {
        char *element = (char *)array_address + element_count * element_size;
        char *beginning = (char *)array_address;
        while (element > beginning) {
            element -= element_size;
            destructor((void *)element);
        }
    }
}

__ATTRIBUTES void *
__cxa_vec_new(size_t element_count, size_t element_size, size_t padding_size,
              void (*constructor)(void *), void (*destructor)(void *))
{
    size_t size = element_count * element_size + padding_size;
    char *base = (char *)malloc(size);
    if (!base) {
        return 0;
    }
    if (padding_size >= sizeof(size_t)) {
        base += padding_size;
        ((size_t *)base)[-1] = element_count;
    }
    __cxa_vec_ctor((void *)base, element_count, element_size, constructor,
                   destructor);
    return (void *)base;
}

__ATTRIBUTES void *
__cxa_vec_new2(size_t element_count, size_t element_size, size_t padding_size,
               void (*constructor)(void *), void (*destructor)(void *),
               void *(*allocator)(size_t), void (*deallocator)(void *))
{
    if (!allocator) {
        return 0;
    }
    size_t size = element_count * element_size + padding_size;
    char *base = (char *)allocator(size);
    if (!base) {
        return 0;
    }
    if (padding_size >= sizeof(size_t)) {
        base += padding_size;
        ((size_t *)base)[-1] = element_count;
    }
    __cxa_vec_ctor((void *)base, element_count, element_size, constructor,
                   destructor);
    return (void *)base;
}

__ATTRIBUTES void *
__cxa_vec_new3(size_t element_count, size_t element_size, size_t padding_size,
               void (*constructor)(void *), void (*destructor)(void *),
               void *(*allocator)(size_t), void (*deallocator)(void *, size_t))
{
    if (!allocator) {
        return 0;
    }
    size_t size = element_count * element_size + padding_size;
    char *base = (char *)allocator(size);
    if (!base) {
        return 0;
    }
    if (padding_size >= sizeof(size_t)) {
        base += padding_size;
        ((size_t *)base)[-1] = element_count;
    }
    __cxa_vec_ctor((void *)base, element_count, element_size, constructor,
                   destructor);
    return (void *)base;
}

__ATTRIBUTES void
__cxa_vec_delete(void *array_address, size_t element_size, size_t padding_size,
                 void (*destructor)(void *))
{
    char *base = (char *)array_address;
    if (padding_size >= sizeof(size_t)) {
        size_t element_count = ((size_t *)array_address)[-1];
        base -= padding_size;
        __cxa_vec_dtor(array_address, element_count, element_size, destructor);
    }
    free((void *)base);
}

__ATTRIBUTES void
__cxa_vec_delete2(void *array_address, size_t element_size, size_t padding_size,
                  void (*destructor)(void *), void (*deallocator)(void *))
{
    char *base = (char *)array_address;
    if (padding_size >= sizeof(size_t)) {
        size_t element_count = ((size_t *)base)[-1];
        base -= padding_size;
        __cxa_vec_dtor(array_address, element_count, element_size, destructor);
    }
    if (deallocator) {
        deallocator((void *)base);
    }
}

__ATTRIBUTES void
__cxa_vec_delete3(void *array_address, size_t element_size, size_t padding_size,
                  void (*destructor)(void *),
                  void (*deallocator)(void *, size_t))
{
    if (padding_size >= sizeof(size_t)) {
        char *base = (char *)array_address;
        size_t element_count = ((size_t *)base)[-1];
        base -= padding_size;
        __cxa_vec_dtor(array_address, element_count, element_size, destructor);
        if (deallocator) {
            deallocator((void *)base, element_count * element_size + padding_size);
        }
    }
}
