/*------------------------------------------------------------------
 * test_memcpy_s
 * File 'mem/memcpy_s.c'
 * Lines executed:86.36% of 22
 *
 *------------------------------------------------------------------
 */

#include "test_private.h"
#include "test_expmem.h"
#if defined(TEST_MSVCRT) && defined(HAVE_MEMCPY_S)
#undef memcpy_s
#endif

#ifdef HAVE_MEMCPY_S
#define HAVE_NATIVE 1
#else
#define HAVE_NATIVE 0
#endif
#include "test_msvcrt.h"

#define LEN (1024)

static uint8_t mem1[LEN + 2];
static uint8_t mem2[LEN + 2];
int test_memcpy_s(void);

int test_memcpy_s(void) {
    errno_t rc;
    uint32_t i;
    rsize_t len;
    int errs = 0;

    /*--------------------------------------------------*/

    for (i = 0; i < LEN; i++) {
        mem1[i] = 33;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 44;
    }
    print_msvcrt(use_msvcrt);

    /* with clang-5 compile-time checks these errors */
#ifndef HAVE_CT_BOS_OVR
    EXPECT_BOS("dest overflow or empty")
    rc = memcpy_s(NULL, LEN, mem2, LEN);
    init_msvcrt(rc == ESNULLP, &use_msvcrt);
    ERR_MSVC(ESNULLP, EINVAL);

    /*--------------------------------------------------*/

    EXPECT_BOS("dest overflow or empty") EXPECT_BOS("slen overflow >dmax")
    rc = memcpy_s(mem1, 0, mem2, LEN);
    ERR_MSVC(ESZEROL, ERANGE); /* and untouched */
    EXPMEM(mem1, 0, LEN, 33, 1);

    /*--------------------------------------------------*/

    EXPECT_BOS("dest overflow or empty")
    rc = memcpy_s(mem1, RSIZE_MAX_MEM + 1, mem2, LEN);
    ERR_MSVC(ESLEMAX, 0); /* and implementation defined */
    if (!use_msvcrt)
        EXPMEM(mem1, 0, LEN, 33, 1);
    else
        EXPMEM(mem1, 0, LEN, 44, 1);

#ifdef HAVE___BUILTIN_OBJECT_SIZE
    for (i = 0; i < LEN; i++) {
        mem1[i] = 33;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 44;
    }

    EXPECT_BOS("dest overflow or empty")
    rc = memcpy_s(mem1, LEN + 3, mem2, LEN);
    ERR(EOVERFLOW); /* limited to destbos */
#ifdef _WIN32
    debug_printf("errno EOVERFLOW=%s\n", strerror(EOVERFLOW));
    debug_printf("errno 132=%s\n", strerror(132));
#endif
    if (!use_msvcrt)
        EXPMEM(mem1, 0, LEN, 33, 1);
    else {
        EXPMEM(mem1, 0, LEN, 44, 1);
    }
#endif

    /*--------------------------------------------------*/

    for (i = 0; i < LEN; i++) {
        mem1[i] = 33;
    }
    EXPECT_BOS("src overflow or empty")
    rc = memcpy_s(mem1, LEN, NULL, LEN);
    ERR_MSVC(ESNULLP, EINVAL); /* and cleared */
    EXPMEM(mem1, 0, LEN, 0, 1);

    EXPECT_BOS("src overflow or empty") EXPECT_BOS("slen overflow >dmax")
    rc = memcpy_s(mem1, LEN, mem2, RSIZE_MAX_MEM + 1);
    ERR_MSVC(ESLEMAX, ERANGE); /* and cleared */
    EXPMEM(mem1, 0, LEN, 0, 1);

    /*--------------------------------------------------*/

    for (i = 0; i < LEN + 1; i++) {
        mem1[i] = 33;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 44;
    }

    len = LEN;
    /* BOS(mem1) == LEN+2 */
    EXPECT_BOS("slen overflow >dmax")
    rc = memcpy_s(mem1, LEN, mem2, (LEN + 1));
    ERR_MSVC(ESNOSPC, ERANGE); /* and cleared */
    EXPMEM(mem1, 0, len + 1, 0, 1);

    for (i = 0; i < LEN + 2; i++) {
        mem1[i] = 33;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 44;
    }

    len = LEN / 2;
    EXPECT_BOS("src overflow or empty") EXPECT_BOS("slen overflow >dmax")
    rc = memcpy_s(mem1, LEN / 2, mem2, LEN + 3);
    ERR_MSVC(ESNOSPC, ERANGE);

    EXPMEM(mem1, 0, len, 0, 1);
    if (mem1[len] != 33) {
        debug_printf("%d - %d m1=%d  m2=%d  \n", __LINE__, (int)len, mem1[len],
                     mem2[len]);
        errs++;
    }
#endif

#ifndef HAVE_CT_BOS_OVR
    for (i = 0; i < LEN; i++) {
        mem1[i] = 55;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 65;
    }

    /* overlap */
    len = 100;
    EXPECT_BOS("dest overlaps with src")
    rc = memcpy_s(&mem1[0], 100, &mem1[10], len);
    ERR_MSVC(ESOVRLP, 0);

    if (!use_msvcrt) {
        EXPMEM(mem1, 0, len, 0, 1);
        if (mem1[len] == 0) {
            debug_printf("%d - %"PRIu32" m1=%d  m2=%d  \n", __LINE__, i, mem1[i],
                         mem2[i]);
            errs++;
        }
    }

    for (i = 0; i < LEN; i++) {
        mem1[i] = 55;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 65;
    }

    /* overlap */
    len = 100;
    EXPECT_BOS("dest overlaps with src")
    rc = memcpy_s(&mem1[10], 100, &mem1[0], 100);
    ERR_MSVC(ESOVRLP, 0);

    if (!use_msvcrt) {
        EXPMEM(mem1, 10, len + 10, 0, 1);
    }
#endif

    /*--------------------------------------------------*/

    for (i = 0; i < LEN; i++) {
        mem1[i] = 33;
    }

    /* check n=0 first */
    rc = memcpy_s(mem1, 10, mem2, 0);
    ERR(EOK); /* and untouched */
    EXPMEM(mem1, 0, LEN, 33, 1);

    rc = memcpy_s(NULL, 10, mem2, 0);
    ERR(EOK); /* and untouched */
    EXPMEM(mem1, 0, LEN, 33, 1);

    rc = memcpy_s(mem1, 0, mem2, 0);
    ERR(EOK); /* and untouched */
    EXPMEM(mem1, 0, LEN, 33, 1);

    rc = memcpy_s(mem1, 10, NULL, 0);
    ERR(EOK); /* and untouched */
    EXPMEM(mem1, 0, LEN, 33, 1);

    /*--------------------------------------------------*/

    for (i = 0; i < LEN + 1; i++) {
        mem1[i] = 33;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 44;
    }

    len = LEN;
    rc = memcpy_s(mem1, len, mem2, len);
    ERR(EOK);
    EXPMEM(mem1, 0, len, 44, 1);
    EXPMEM(mem1, len, LEN + 1, 33, 1);

    for (i = 0; i < LEN + 1; i++) {
        mem1[i] = 33;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 44;
    }

    len = 1;
    rc = memcpy_s(mem1, len, mem2, len);
    ERR(EOK);
    EXPMEM(mem1, 0, len, 44, 1);
    EXPMEM(mem1, len, LEN + 1, 33, 1);

    /*--------------------------------------------------*/

    for (i = 0; i < LEN; i++) {
        mem1[i] = 55;
    }

    /* same ptr - no move */
    GCC_PUSH_WARN_RESTRICT
    rc = memcpy_s(mem1, LEN, mem1, LEN);
    GCC_POP_WARN_RESTRICT
    ERR(EOK);

    /*--------------------------------------------------*/

    for (i = 0; i < LEN; i++) {
        mem1[i] = 35;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 55;
    }

    len = 5;
    rc = memcpy_s(mem1, len, mem2, len);
    ERR(EOK)
    else {
        EXPMEM(mem1, 0, len, 55, 1);
    }

    /*--------------------------------------------------*/

    for (i = 0; i < LEN; i++) {
        mem1[i] = 35;
    }
    for (i = 0; i < LEN; i++) {
        mem2[i] = 55;
    }

    rc = memcpy_s(mem1, LEN, mem2, LEN / 2);
    ERR(EOK)
    else {
        EXPMEM(mem1, 0, LEN / 2, 55, 1);
    }

    /*--------------------------------------------------*/

    return (errs);
}

#ifndef __KERNEL__
/* simple hack to get this to work for both userspace and Linux kernel,
   until a better solution can be created. */
int main(void) { return (test_memcpy_s()); }
#endif
