/*
 * Copyright (c) Valkey Contributors
 * All rights reserved.
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "generated_wrappers.hpp"

#include <cstdint>
#include <cstdio>

extern "C" {
#include "zmalloc.h"
}

class ZmallocTest : public ::testing::Test {};

TEST_F(ZmallocTest, TestZmallocAllocReallocCallocAndFree) {
    size_t used_memory_before = zmalloc_used_memory();
    void *ptr, *ptr2;

    ptr = zmalloc(123);
    printf("Allocated 123 bytes; used: %lld\n",
           (long long)zmalloc_used_memory() - used_memory_before);

    ptr = zrealloc(ptr, 456);
    printf("Reallocated to 456 bytes; used: %lld\n",
           (long long)zmalloc_used_memory() - used_memory_before);

    ptr2 = zcalloc(123);
    printf("Callocated 123 bytes; used: %lld\n",
           (long long)zmalloc_used_memory() - used_memory_before);

    zfree(ptr);
    zfree(ptr2);
    printf("Freed pointers; used: %lld\n",
           (long long)zmalloc_used_memory() - used_memory_before);

    ASSERT_EQ(zmalloc_used_memory(), used_memory_before);
}

TEST_F(ZmallocTest, TestZmallocAllocZeroByteAndFree) {
    size_t used_memory_before = zmalloc_used_memory();
    void *ptr;

    ptr = zmalloc(0);
    printf("Allocated 0 bytes; used: %zu\n", zmalloc_used_memory());
    zfree(ptr);

    ASSERT_EQ(zmalloc_used_memory(), used_memory_before);
}

TEST_F(ZmallocTest, TestZmallocCacheAlignedAllocAndFree) {
    size_t used_memory_before = zmalloc_used_memory();
    void *ptr = zmalloc_cache_aligned(123);
    uintptr_t alignment;
    size_t usable_size;

    ASSERT_NE(ptr, nullptr);
    alignment = (uintptr_t)ptr % CACHE_LINE_SIZE;
    usable_size = zmalloc_usable_size(ptr);

    zfree(ptr);

    ASSERT_EQ(alignment, 0u);
    ASSERT_GE(usable_size, 123u);
    ASSERT_EQ(zmalloc_used_memory(), used_memory_before);
}

TEST_F(ZmallocTest, TestZmallocExternalUsedMemory) {
    size_t used_memory_before = zmalloc_used_memory();
    size_t external_memory_before = zmalloc_used_external_memory();

    ASSERT_EQ(zmalloc_increase_used_memory_external(123), 0);
    ASSERT_EQ(zmalloc_used_external_memory(), external_memory_before + 123);
    ASSERT_EQ(zmalloc_used_memory(), used_memory_before + 123);

    ASSERT_EQ(zmalloc_decrease_used_memory_external(123), 0);
    ASSERT_EQ(zmalloc_used_external_memory(), external_memory_before);
    ASSERT_EQ(zmalloc_used_memory(), used_memory_before);
}

TEST_F(ZmallocTest, TestZmallocExternalUsedMemoryBounds) {
    size_t external_memory_before = zmalloc_used_external_memory();

    ASSERT_EQ(zmalloc_decrease_used_memory_external(external_memory_before + 1), -1);
    if (external_memory_before == 0) {
        ASSERT_EQ(zmalloc_increase_used_memory_external(1), 0);
        ASSERT_EQ(zmalloc_increase_used_memory_external(SIZE_MAX), -1);
        ASSERT_EQ(zmalloc_decrease_used_memory_external(1), 0);
    } else {
        ASSERT_EQ(zmalloc_increase_used_memory_external(SIZE_MAX - external_memory_before + 1), -1);
    }
    ASSERT_EQ(zmalloc_used_external_memory(), external_memory_before);
}
