diff options
Diffstat (limited to 'android/async-utils.c')
-rw-r--r-- | android/async-utils.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/android/async-utils.c b/android/async-utils.c new file mode 100644 index 0000000..678b0b4 --- /dev/null +++ b/android/async-utils.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "android/async-utils.h" +#include "unistd.h" + +void +asyncReader_init(AsyncReader* ar, + void* buffer, + size_t buffsize, + LoopIo* io) +{ + ar->buffer = buffer; + ar->buffsize = buffsize; + ar->pos = 0; + if (buffsize > 0) + loopIo_wantRead(io); +} + +AsyncStatus +asyncReader_read(AsyncReader* ar, + LoopIo* io) +{ + int ret; + + if (ar->pos >= ar->buffsize) { + return ASYNC_COMPLETE; + } + + do { + ret = read(io->fd, ar->buffer + ar->pos, ar->buffsize - ar->pos); + if (ret == 0) { + /* disconnection ! */ + errno = ECONNRESET; + return ASYNC_ERROR; + } + if (ret < 0) { + if (errno == EINTR) /* loop on EINTR */ + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) { + loopIo_wantRead(io); + return ASYNC_NEED_MORE; + } + return ASYNC_ERROR; + } + ar->pos += ret; + + } while (ar->pos < ar->buffsize); + + loopIo_dontWantRead(io); + return ASYNC_COMPLETE; +} + +void +asyncWriter_init(AsyncWriter* aw, + const void* buffer, + size_t buffsize, + LoopIo* io) +{ + aw->buffer = buffer; + aw->buffsize = buffsize; + aw->pos = 0; + if (buffsize > 0) + loopIo_wantWrite(io); +} + +AsyncStatus +asyncWriter_write(AsyncWriter* aw, + LoopIo* io) +{ + int ret; + + if (aw->pos >= aw->buffsize) { + return ASYNC_COMPLETE; + } + + do { + ret = write(io->fd, aw->buffer + aw->pos, aw->buffsize - aw->pos); + if (ret == 0) { + /* disconnection ! */ + errno = ECONNRESET; + return ASYNC_ERROR; + } + if (ret < 0) { + if (errno == EINTR) /* loop on EINTR */ + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return ASYNC_NEED_MORE; + } + return ASYNC_ERROR; + } + aw->pos += ret; + + } while (aw->pos < aw->buffsize); + + loopIo_dontWantWrite(io); + return ASYNC_COMPLETE; +} + + +void +asyncLineReader_init(AsyncLineReader* alr, + void* buffer, + size_t buffsize, + LoopIo* io) +{ + alr->buffer = buffer; + alr->buffsize = buffsize; + alr->pos = 0; + if (buffsize > 0) + loopIo_wantRead(io); +} + +AsyncStatus +asyncLineReader_read(AsyncLineReader* alr, + LoopIo* io) +{ + int ret; + + if (alr->pos >= alr->buffsize) { + errno = ENOMEM; + return ASYNC_ERROR; + } + + do { + char ch; + ret = read(io->fd, &ch, 1); + if (ret == 0) { + /* disconnection ! */ + errno = ECONNRESET; + return ASYNC_ERROR; + } + if (ret < 0) { + if (errno == EINTR) /* loop on EINTR */ + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) { + loopIo_wantRead(io); + return ASYNC_NEED_MORE; + } + return ASYNC_ERROR; + } + alr->buffer[alr->pos++] = (uint8_t)ch; + if (ch == '\n') { + loopIo_dontWantRead(io); + return ASYNC_COMPLETE; + } + } while (alr->pos < alr->buffsize); + + /* Not enough room in the input buffer!*/ + loopIo_dontWantRead(io); + errno = ENOMEM; + return ASYNC_ERROR; +} + +const char* +asyncLineReader_getLineRaw(AsyncLineReader* alr, int *pLength) +{ + if (alr->pos == 0 || alr->pos > alr->buffsize) + return NULL; + + if (pLength != 0) + *pLength = alr->pos; + + return (const char*) alr->buffer; +} + +const char* +asyncLineReader_getLine(AsyncLineReader* alr) +{ + /* Strip trailing \n if any */ + size_t pos = alr->pos; + char* buffer = (char*) alr->buffer; + + if (pos == 0 || pos > alr->buffsize) + return NULL; + + pos--; + + /* Check that we have a proper terminator, and replace it with 0 */ + if (buffer[pos] != '\n') + return NULL; + + buffer[pos] = '\0'; + + /* Also strip \r\n */ + if (pos > 0 && buffer[--pos] == '\r') { + buffer[pos] = '\0'; + } + + return (const char*) buffer; +} + + +enum { + CONNECT_ERROR = 0, + CONNECT_CONNECTING, + CONNECT_COMPLETED +}; + +AsyncStatus +asyncConnector_init(AsyncConnector* ac, + const SockAddress* address, + LoopIo* io) +{ + int ret; + ac->error = 0; + ret = socket_connect(io->fd, address); + if (ret == 0) { + ac->state = CONNECT_COMPLETED; + return ASYNC_COMPLETE; + } + if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) { + ac->state = CONNECT_CONNECTING; + /* The socket will be marked writable for select() when the + * connection is established, or when it is definitely + * refused / timed-out, for any reason. */ + loopIo_wantWrite(io); + return ASYNC_NEED_MORE; + } + ac->error = errno; + ac->state = CONNECT_ERROR; + return ASYNC_ERROR; +} + +AsyncStatus +asyncConnector_run(AsyncConnector* ac, LoopIo* io) +{ + switch (ac->state) { + case CONNECT_ERROR: + errno = ac->error; + return ASYNC_ERROR; + + case CONNECT_CONNECTING: + loopIo_dontWantWrite(io); + /* We need to read the socket error to determine if + * the connection was really succesful or not. This + * is optional, because in case of error a future + * read() or write() will fail anyway, but this + * allows us to get a better error value as soon as + * possible. + */ + ac->error = socket_get_error(io->fd); + if (ac->error == 0) { + ac->state = CONNECT_COMPLETED; + return ASYNC_COMPLETE; + } + ac->state = CONNECT_ERROR; + errno = ac->error; + return ASYNC_ERROR; + + default: + return ASYNC_COMPLETE; + } +} |