aboutsummaryrefslogtreecommitdiffstats
path: root/android/async-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/async-utils.c')
-rw-r--r--android/async-utils.c266
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;
+ }
+}