#include "Internal.h" #include #include #include #define INITIAL_CAPACITY 4 static int wsaToErrorCode(int value) { switch (value) { case 0: return CGE_NET_EOK; case WSAEINVAL: return CGE_NET_EINVAL; case WSAENOBUFS: case WSA_NOT_ENOUGH_MEMORY: return CGE_NET_ENOMEM; case WSAEWOULDBLOCK: return CGE_NET_EWOULDBLOCK; default: return CGE_NET_EUNKNOWN; } } CgeNetPoll *CgeNetPollCreate(int *result) { CgeNetPoll *poll; if (!(poll = malloc(sizeof(CgeNetPoll)))) { if (result) *result = CGE_NET_ENOMEM; return NULL; } poll->data = malloc(INITIAL_CAPACITY * sizeof(struct PollData)); poll->pfd = malloc(INITIAL_CAPACITY * sizeof(WSAPOLLFD)); if (!poll->data || !poll->pfd) { free(poll->data); free(poll->pfd); free(poll); if (result) *result = CGE_NET_ENOMEM; return NULL; } poll->size = 0; poll->capacity = INITIAL_CAPACITY; poll->lastError = CGE_NET_EOK; if (result) *result = CGE_NET_EOK; return poll; } void CgeNetPollFree(CgeNetPoll *poll) { if (!poll) return; free(poll->pfd); free(poll->data); free(poll); } int CgeNetPollErrorCode(CgeNetPoll *poll) { assert(poll != NULL); return poll->lastError; } static int ensureCapacity(CgeNetPoll *poll) { struct PollData *newData; WSAPOLLFD *newPfd; size_t newCapacity; assert(poll != NULL); assert(poll->capacity > 0); if (poll->size < poll->capacity) return 1; newCapacity = poll->capacity * 2; newData = malloc(newCapacity * sizeof(struct PollData)); newPfd = malloc(newCapacity * sizeof(WSAPOLLFD)); if (!newData || !newPfd) { free(newData); free(newPfd); poll->lastError = CGE_NET_ENOMEM; return 0; } else { memcpy(newData, poll->data, poll->size * sizeof(struct PollData)); memcpy(newPfd, poll->pfd, poll->size * sizeof(WSAPOLLFD)); free(poll->data); free(poll->pfd); } poll->data = newData; poll->pfd = newPfd; poll->capacity = newCapacity; return 1; } int CgeNetPollAdd(CgeNetPoll *poll, CgeNetSocket *socket, CgeNetCallback callback, int events) { assert(poll != NULL); assert(socket != NULL); assert(socket->handle != INVALID_SOCKET); assert(callback != NULL); assert(events & CGE_NET_READABLE || events & CGE_NET_WRITABLE); if (socket->pollObject) { poll->lastError = CGE_NET_EINVAL; return 0; } if (!ensureCapacity(poll)) return 0; assert(poll->size + 1 <= poll->capacity); socket->pollObject = poll; socket->pollIndex = poll->size; poll->data[poll->size].socket = socket; poll->data[poll->size].callback = callback; poll->data[poll->size].events = events; poll->pfd[poll->size].fd = socket->handle; poll->pfd[poll->size].events = 0; poll->pfd[poll->size].revents = 0; if (events & CGE_NET_READABLE) poll->pfd[poll->size].events |= POLLIN; if (events & CGE_NET_WRITABLE) poll->pfd[poll->size].events |= POLLOUT; poll->size++; return 1; } static void processEvent(CgeNetPoll *poll, struct PollData *data) { size_t index; int events, revents; assert(poll != NULL); assert(data != NULL); assert(data >= poll->data && data < poll->data + poll->size); index = data - poll->data; revents = poll->pfd[index].revents; events = poll->pfd[index].revents = 0; poll->pfd[index].events = 0; if (data->events & CGE_NET_READABLE) poll->pfd[index].events |= POLLIN; if (data->events & CGE_NET_WRITABLE) poll->pfd[index].events |= POLLOUT; if (revents & (POLLIN | POLLERR | POLLHUP)) events |= CGE_NET_READABLE; if (revents & POLLOUT) events |= CGE_NET_WRITABLE; if (events) data->callback(data->socket, events); } int CgeNetPollRemove(CgeNetPoll *poll, CgeNetSocket *socket) { size_t index; assert(poll != NULL); assert(socket != NULL); assert(socket->handle != INVALID_SOCKET); if (socket->pollObject != poll) { poll->lastError = CGE_NET_EINVAL; return 0; } assert(poll->size > 0); index = socket->pollIndex; socket->pollObject = NULL; socket->pollIndex = 0; poll->size--; if (index < poll->size) { poll->data[poll->size].socket->pollIndex = index; poll->data[index] = poll->data[poll->size]; poll->pfd[index] = poll->pfd[poll->size]; processEvent(poll, poll->data + index); } return 1; } int CgeNetPollWait(CgeNetPoll *p, int timeout) { int ready; size_t i; assert(p != NULL); if (p->size == 0) return 1; if ((ready = WSAPoll(p->pfd, (ULONG)p->size, timeout)) == SOCKET_ERROR) { p->lastError = wsaToErrorCode(WSAGetLastError()); return 0; } for (i = 0; i < p->size && ready > 0; i++) { processEvent(p, p->data + i); } return 1; }