packages feed

winio-0.1: cbits/HsWinIO.c

// Copyright 2007-2008 Felix Martini.
//
// See LICENSE for license terms.

#ifdef _WIN32

#define _WIN32_WINNT 0x0500

#define DllExport __declspec(dllexport)

#include <winsock2.h>
#include <windows.h>

DllExport
BOOL HsGetFileSize(HANDLE fileHandle, INT64 *fileSize)
{
    LARGE_INTEGER size;
    BOOL res;
    res = GetFileSizeEx(fileHandle, &size);
    *fileSize = size.QuadPart;
    return res;
}

DllExport
BOOL HsGetFilePointer(HANDLE fileHandle, INT64 *filePointer)
{
    LARGE_INTEGER zero;
    LARGE_INTEGER pointer;
    BOOL res;
    zero.QuadPart = 0;
    res = SetFilePointerEx(fileHandle, zero, &pointer, FILE_CURRENT);
    *filePointer = pointer.QuadPart;
    return res;
}

DllExport
BOOL HsSetFilePointer(HANDLE fileHandle, INT64 offset, DWORD moveMethod)
{
    LARGE_INTEGER distToMove;
    distToMove.QuadPart = offset;
    return SetFilePointerEx(fileHandle, distToMove, NULL, moveMethod);
}

DllExport
HANDLE HsNewCompletionPort(DWORD numThreads)
{
    return CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
               numThreads);
}

DllExport
BOOL HsAssociateHandleWithPort(HANDLE handle, HANDLE port)
{
    HANDLE h;
    h = CreateIoCompletionPort(handle, port, 0, 0);
    return (h == port);
}

DllExport
BOOL HsAssociateSocketWithPort(SOCKET socket, HANDLE port)
{
    HANDLE h;
    h = CreateIoCompletionPort((HANDLE)socket, port, 0, 0);
    return (h == port);
}

typedef struct _PER_IO_DATA {
    OVERLAPPED Overlapped;
    void       *UserData;
} PER_IO_DATA;

DllExport
BOOL HsGetCompletion(HANDLE port, DWORD *numBytes, void **userData,
         DWORD timeout)
{
    OVERLAPPED *overlappedPtr = NULL;
    ULONG_PTR completionKey;
    BOOL res;
    
    res = GetQueuedCompletionStatus(port, numBytes, &completionKey,
              &overlappedPtr, timeout);
    if (overlappedPtr != NULL) {
        PER_IO_DATA *perIoDataPtr;
        perIoDataPtr =
            CONTAINING_RECORD(overlappedPtr, PER_IO_DATA, Overlapped);
        *userData = perIoDataPtr->UserData;
        HeapFree(GetProcessHeap(), 0, perIoDataPtr);
    } else {
        *userData = NULL;
    }
    return res;
}

// HsAsyncRead returns the number of bytes read if the operation is completed
// synchronously or -1 if an error occurs.
// N.B. Operations that are successfully initiated asynchronously will report
// the error ERROR_IO_PENDING and a completion event will be send to the
// completion port when the operation is finished.
DllExport
int HsAsyncRead(HANDLE handle, UINT64 pos, void *buf, int size,
        void *userData)
{
    PER_IO_DATA *perIoDataPtr;
    DWORD numBytesRead;
    BOOL res;
    
    perIoDataPtr =
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_DATA));
    if (perIoDataPtr == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return -1;
    } else {
        perIoDataPtr->Overlapped.Offset = (DWORD) pos;
        perIoDataPtr->Overlapped.OffsetHigh = (DWORD) (pos >> 32);
        perIoDataPtr->UserData = userData;
        res = ReadFile(handle, buf, size, &numBytesRead,
                  &(perIoDataPtr->Overlapped));
        if (!res) {
            DWORD error;
            error = GetLastError();
            if (error != ERROR_IO_PENDING) {
                HeapFree(GetProcessHeap(), 0, perIoDataPtr);
            }
            return -1;
        } else {
            // We report a pending operation even if it has completed
            // synchronously because Windows still posts a completion event
            // to the port. TODO: Vista and later operating systems support a
            // function named SetFileCompletionNotificationModes which can
            // prevent that.
            SetLastError(ERROR_IO_PENDING);
            return -1;
        }
    }
}

DllExport
int HsAsyncWrite(HANDLE handle, UINT64 pos, void *buf, int size,
        void *userData)
{
    PER_IO_DATA *perIoDataPtr;
    DWORD numBytesWritten;
    BOOL res;
    
    perIoDataPtr =
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_DATA));
    if (perIoDataPtr == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return -1;
    } else {
        perIoDataPtr->Overlapped.Offset = (DWORD) pos;
        perIoDataPtr->Overlapped.OffsetHigh = (DWORD) (pos >> 32);
        perIoDataPtr->UserData = userData;
        res = WriteFile(handle, buf, size, &numBytesWritten,
                  &(perIoDataPtr->Overlapped));
        if (!res) {
            DWORD error;
            error = GetLastError();
            if (error != ERROR_IO_PENDING) {
                HeapFree(GetProcessHeap(), 0, perIoDataPtr);
            }
            return -1;
        } else {
            SetLastError(ERROR_IO_PENDING);
            return -1;
        }
    }
}

DllExport
int HsAsyncRecv(SOCKET socket, void *buf, int size, void *userData)
{
    WSABUF dataBuf;
    DWORD flags;
    PER_IO_DATA *perIoDataPtr;
    DWORD numBytesRecvd;
    int res;
    
    dataBuf.buf = (char *)buf;
    dataBuf.len = size;
    flags = 0;
    perIoDataPtr =
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_DATA));
    if (perIoDataPtr == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return -1;
    } else {
        perIoDataPtr->UserData = userData;
        res = WSARecv(socket, &dataBuf, 1, &numBytesRecvd, &flags,
                  &(perIoDataPtr->Overlapped), NULL);
        if (res != 0) {
            DWORD error;
            error = WSAGetLastError();
            if (error != WSA_IO_PENDING) {
                HeapFree(GetProcessHeap(), 0, perIoDataPtr);
            }
            return -1;
        } else {
            SetLastError(ERROR_IO_PENDING);
            return -1;
        }
    }
}

DllExport
int HsAsyncRecvFrom(SOCKET socket, void *buf, int size,
        struct sockaddr *fromPtr, int *fromLenPtr, void *userData)
{
    WSABUF dataBuf;
    DWORD flags;
    PER_IO_DATA *perIoDataPtr;
    DWORD numBytesRecvd;
    int res;

    dataBuf.buf = (char *)buf;
    dataBuf.len = size;
    flags = 0;
    perIoDataPtr =
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_DATA));
    if (perIoDataPtr == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return -1;
    } else {
        perIoDataPtr->UserData = userData;
        res = WSARecvFrom(socket, &dataBuf, 1, &numBytesRecvd, &flags,
                 fromPtr, fromLenPtr, &(perIoDataPtr->Overlapped), NULL);
        if (res != 0) {
            DWORD error;
            error = WSAGetLastError();
            if (error != WSA_IO_PENDING) {
                HeapFree(GetProcessHeap(), 0, perIoDataPtr);
            }
            return -1;
        } else {
            SetLastError(ERROR_IO_PENDING);
            return -1;
        }
    }
}

DllExport
int HsAsyncSend(SOCKET socket, void *buf, int size, void *userData)
{
    WSABUF dataBuf;
    DWORD flags;
    PER_IO_DATA *perIoDataPtr;
    DWORD numBytesSend;
    int res;
    
    dataBuf.buf = (char *)buf;
    dataBuf.len = size;
    flags = 0;
    perIoDataPtr =
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_DATA));
    if (perIoDataPtr == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return -1;
    } else {
        perIoDataPtr->UserData = userData;
        res = WSASend(socket, &dataBuf, 1, &numBytesSend, flags,
                  &(perIoDataPtr->Overlapped), NULL);
        if (res != 0) {
            DWORD error;
            error = WSAGetLastError();
            if (error != WSA_IO_PENDING) {
                HeapFree(GetProcessHeap(), 0, perIoDataPtr);
            }
            return -1;
        } else {
            SetLastError(ERROR_IO_PENDING);
            return -1;
        }
    }
}

DllExport
int HsAsyncSendTo(SOCKET socket, void *buf, int size,
        struct sockaddr *toPtr, int toLen, void *userData)
{
    WSABUF dataBuf;
    DWORD flags;
    PER_IO_DATA *perIoDataPtr;
    DWORD numBytesSend;
    int res;

    dataBuf.buf = (char *)buf;
    dataBuf.len = size;
    flags = 0;
    perIoDataPtr =
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_DATA));
    if (perIoDataPtr == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return -1;
    } else {
        perIoDataPtr->UserData = userData;
        res = WSASendTo(socket, &dataBuf, 1, &numBytesSend, flags,
                  toPtr, toLen, &(perIoDataPtr->Overlapped), NULL);
        if (res != 0) {
            DWORD error;
            error = WSAGetLastError();
            if (error != WSA_IO_PENDING) {
                HeapFree(GetProcessHeap(), 0, perIoDataPtr);
            }
            return -1;
        } else {
            SetLastError(ERROR_IO_PENDING);
            return -1;
        }
    }
}

#endif // _WIN32