/*
 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
 *
 * Squid software is distributed under GPLv2+ license and includes
 * contributions from numerous individuals and organizations.
 * Please see the COPYING and CONTRIBUTORS files for details.
 */

/* DEBUG: section 81    aio_xxx() POSIX emulation on Windows */

#include "squid.h"
#include "comm.h"
#include "DiskIO/AIO/aio_win32.h"
#include "fd.h"
#include "StatCounters.h"
#include "win32.h"

#include <cerrno>

#if _SQUID_WINDOWS_
VOID CALLBACK IoCompletionRoutine(DWORD dwErrorCode,
                                  DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{

    struct aiocb *aiocbp = (struct aiocb *) lpOverlapped->hEvent;

    aiocbp->aio_sigevent.sigev_notify = dwErrorCode;
    aiocbp->aio_sigevent.sigev_signo = dwNumberOfBytesTransfered;
    debugs(81, 7, "AIO operation complete: errorcode=" << dwErrorCode << " nbytes=" << dwNumberOfBytesTransfered);
    xfree(lpOverlapped);
}

int aio_read(struct aiocb *aiocbp)
{
    LPOVERLAPPED Overlapped;
    BOOL IoOperationStatus;

    /* Allocate an overlapped structure. */
    Overlapped = (LPOVERLAPPED) xcalloc(1, sizeof(OVERLAPPED));

    if (!Overlapped) {
        errno = ENOMEM;
        return -1;
    }

#if _FILE_OFFSET_BITS==64
#ifdef __GNUC__
    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000LL);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000LL);

#else

    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000);

#endif
#else

    Overlapped->Offset = aiocbp->aio_offset;

    Overlapped->OffsetHigh = 0;

#endif

    Overlapped->hEvent = aiocbp;

    aiocbp->aio_sigevent.sigev_notify = EINPROGRESS;

    aiocbp->aio_sigevent.sigev_signo = -1;

    IoOperationStatus = ReadFileEx((HANDLE)_get_osfhandle(aiocbp->aio_fildes),
                                   aiocbp->aio_buf,
                                   aiocbp->aio_nbytes,
                                   Overlapped,
                                   IoCompletionRoutine);

    /* Test to see if the I/O was queued successfully. */
    if (!IoOperationStatus) {
        errno = GetLastError();
        debugs(81, DBG_IMPORTANT, "aio_read: GetLastError=" << errno  );
        return -1;
    }

    /* The I/O queued successfully. Go back into the
       alertable wait for I/O completion or for
       more I/O requests. */
    return 0;
}

int aio_read64(struct aiocb64 *aiocbp)
{
    LPOVERLAPPED Overlapped;
    BOOL IoOperationStatus;

    /* Allocate an overlapped structure. */
    Overlapped = (LPOVERLAPPED) xcalloc(1, sizeof(OVERLAPPED));

    if (!Overlapped) {
        errno = ENOMEM;
        return -1;
    }

#ifdef __GNUC__
    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000LL);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000LL);

#else

    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000);

#endif

    Overlapped->hEvent = aiocbp;

    aiocbp->aio_sigevent.sigev_notify = EINPROGRESS;

    aiocbp->aio_sigevent.sigev_signo = -1;

    IoOperationStatus = ReadFileEx((HANDLE)_get_osfhandle(aiocbp->aio_fildes),
                                   aiocbp->aio_buf,
                                   aiocbp->aio_nbytes,
                                   Overlapped,
                                   IoCompletionRoutine);

    /* Test to see if the I/O was queued successfully. */
    if (!IoOperationStatus) {
        errno = GetLastError();
        debugs(81, DBG_IMPORTANT, "aio_read: GetLastError=" << errno  );
        return -1;
    }

    /* The I/O queued successfully. Go back into the
       alertable wait for I/O completion or for
       more I/O requests. */
    return 0;
}

int aio_write(struct aiocb *aiocbp)
{
    LPOVERLAPPED Overlapped;
    BOOL IoOperationStatus;

    /* Allocate an overlapped structure. */
    Overlapped = (LPOVERLAPPED) xcalloc(1, sizeof(OVERLAPPED));

    if (!Overlapped) {
        errno = ENOMEM;
        return -1;
    }

#if _FILE_OFFSET_BITS==64
#ifdef __GNUC__
    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000LL);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000LL);

#else

    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000);

#endif
#else

    Overlapped->Offset = aiocbp->aio_offset;

    Overlapped->OffsetHigh = 0;

#endif

    Overlapped->hEvent = aiocbp;

    aiocbp->aio_sigevent.sigev_notify = EINPROGRESS;

    aiocbp->aio_sigevent.sigev_signo = -1;

    IoOperationStatus = WriteFileEx((HANDLE)_get_osfhandle(aiocbp->aio_fildes),
                                    aiocbp->aio_buf,
                                    aiocbp->aio_nbytes,
                                    Overlapped,
                                    IoCompletionRoutine);

    /* Test to see if the I/O was queued successfully. */
    if (!IoOperationStatus) {
        errno = GetLastError();
        debugs(81, DBG_IMPORTANT, "aio_write: GetLastError=" << errno  );
        return -1;
    }

    /* The I/O queued successfully. Go back into the
       alertable wait for I/O completion or for
       more I/O requests. */
    return 0;
}

int aio_write64(struct aiocb64 *aiocbp)
{
    LPOVERLAPPED Overlapped;
    BOOL IoOperationStatus;

    /* Allocate an overlapped structure. */
    Overlapped = (LPOVERLAPPED) xcalloc(1, sizeof(OVERLAPPED));

    if (!Overlapped) {
        errno = ENOMEM;
        return -1;
    }

#ifdef __GNUC__
    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000LL);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000LL);

#else

    Overlapped->Offset = (DWORD) (aiocbp->aio_offset % 0x100000000);

    Overlapped->OffsetHigh = (DWORD) (aiocbp->aio_offset / 0x100000000);

#endif

    Overlapped->hEvent = aiocbp;

    aiocbp->aio_sigevent.sigev_notify = EINPROGRESS;

    aiocbp->aio_sigevent.sigev_signo = -1;

    IoOperationStatus = WriteFileEx((HANDLE)_get_osfhandle(aiocbp->aio_fildes),
                                    aiocbp->aio_buf,
                                    aiocbp->aio_nbytes,
                                    Overlapped,
                                    IoCompletionRoutine);

    /* Test to see if the I/O was queued successfully. */
    if (!IoOperationStatus) {
        errno = GetLastError();
        debugs(81, DBG_IMPORTANT, "aio_write: GetLastError=" << errno  );
        return -1;
    }

    /* The I/O queued successfully. Go back into the
       alertable wait for I/O completion or for
       more I/O requests. */
    return 0;
}

int aio_error(const struct aiocb * aiocbp)
{
    return aiocbp->aio_sigevent.sigev_notify;
}

int aio_error64(const struct aiocb64 * aiocbp)
{
    return aiocbp->aio_sigevent.sigev_notify;
}

int aio_open(const char *path, int mode)
{
    HANDLE hndl;
    DWORD dwCreationDisposition;
    DWORD dwDesiredAccess;
    int fd;

    if (mode & O_WRONLY)
        mode |= O_APPEND;

    mode |= O_BINARY;

    errno = 0;

    if (mode & O_WRONLY)
        dwDesiredAccess = GENERIC_WRITE;
    else
        dwDesiredAccess = (mode & O_RDONLY) ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE;

    if (mode & O_TRUNC)
        dwCreationDisposition = CREATE_ALWAYS;
    else
        dwCreationDisposition = (mode & O_CREAT) ? OPEN_ALWAYS : OPEN_EXISTING;

    if ((hndl = CreateFile(path,                    /* file name               */
                           dwDesiredAccess,         /* access mode             */
                           0,                       /* share mode              */
                           NULL,                    /* SD                      */
                           dwCreationDisposition,   /* how to create           */
                           FILE_FLAG_OVERLAPPED,    /* file attributes         */
                           NULL                     /* handle to template file */
                          )) != INVALID_HANDLE_VALUE) {
        ++ statCounter.syscalls.disk.opens;
        fd = _open_osfhandle((long) hndl, 0);
        commSetCloseOnExec(fd);
        fd_open(fd, FD_FILE, path);
    } else {
        errno = GetLastError();
        fd = DISK_ERROR;
    }

    return fd;
}

void aio_close(int fd)
{
    CloseHandle((HANDLE)_get_osfhandle(fd));
    fd_close(fd);
    ++ statCounter.syscalls.disk.closes;
}

ssize_t aio_return(struct aiocb * aiocbp)
{
    return aiocbp->aio_sigevent.sigev_signo;
}

ssize_t aio_return64(struct aiocb64 * aiocbp)

{
    return aiocbp->aio_sigevent.sigev_signo;
}
#endif /* _SQUID_WINDOWS_ */

