// Copyright 2007-2008 Felix Martini. // // See LICENSE for license terms. #ifdef _WIN32 #define _WIN32_WINNT 0x0500 #define DllExport __declspec(dllexport) #include #include 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