src/lib/transport_test.c
branchnew-lib-errors
changeset 219 cefec18b8268
parent 196 873796250c60
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport_test.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,366 @@
+#include "transport_test.h"
+#include "transport_internal.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+
+/**
+ * Simple IO vector
+ */
+struct io_vec {
+    /** The buffer */
+    char *buf;
+
+    /** Buffer size */
+    size_t len;
+};
+
+/**
+ * Simple vectored IO-buffer
+ */
+struct io_buf {
+    /** The array of buffer-vectors, {NULL}-terminated */
+    struct io_vec *vecs;
+
+    /** The number of io_vecs */
+    size_t count;
+
+    /** Current read/write vector */
+    struct io_vec *read_vec, *write_vec;
+
+    /** Offset into current vector */
+    size_t off;
+};
+
+/**
+ * Forward-declare our transport_type
+ */
+extern const struct transport_type transport_test_type;
+
+
+/**
+ * A dummy sock_stream implementation intended for testing purposes.
+ */
+struct transport_test {
+    /** The base transport stuff */
+    struct transport base;
+
+    /** The send/recieve buffers */
+    struct io_buf send_buf, recv_buf;
+
+    /** No more data is going to be added, return EOF once all the rest is consumed */
+    bool eof;
+};
+
+/**
+ * Get a transport pointer from a transport_test pointer
+ */
+#define TRANSPORT_TEST_BASE(tp_ptr) (&(tp_ptr)->base)
+
+/**
+ * Grow buf->vecs if needed to ensure that buf->write_vec points to a valid io_vec
+ */
+static err_t io_buf_grow (struct io_buf *buf)
+{
+    size_t read_vec_offset = buf->read_vec ? (buf->read_vec - buf->vecs) : 0;
+    size_t write_vec_offset = buf->write_vec ? (buf->write_vec - buf->vecs) : 0;
+    struct io_vec *v;
+    struct io_vec *vecs_tmp = buf->vecs;
+
+    // don't grow if not full
+    if (buf->vecs && buf->write_vec < buf->vecs + buf->count)
+        return SUCCESS;
+
+    // new size
+    buf->count = buf->count * 2 + 1;
+
+    // grow
+    if ((buf->vecs = realloc(buf->vecs, buf->count * sizeof(struct io_vec))) == NULL) {
+        // restore old value
+        buf->vecs = vecs_tmp;
+
+        return ERR_CALLOC;
+    }
+
+    // restore vec positions
+    buf->write_vec = buf->vecs + write_vec_offset;
+    buf->read_vec = buf->vecs + read_vec_offset;
+
+    // zero new vecs
+    for (v = buf->write_vec; v < buf->vecs + buf->count; v++)
+        memset(v, 0, sizeof(*v));
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Write some data to an io_buf, copying it.
+ */
+static err_t io_buf_write (struct io_buf *buf, const char *data, size_t len)
+{
+    error_t err;
+
+    // ensure there's room
+    if ((ERROR_CODE(&err) = io_buf_grow(buf)))
+        goto error;
+    
+    // the vector to use
+    struct io_vec *vec = buf->write_vec;
+
+    // allocate
+    if ((vec->buf = malloc(len)) == NULL)
+        JUMP_SET_ERROR(&err, ERR_MEM);
+    
+    // store
+    vec->len = len;
+    memcpy(vec->buf, data, len);
+
+    // vec consumed
+    buf->write_vec++;
+
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(&err);
+}
+
+/**
+ * Destroy the io_buf, freeing all resources.
+ *
+ * The io_buf must not be used anymore.
+ */
+static void io_buf_destroy (struct io_buf *buf)
+{
+    size_t i;
+
+    // free the io_vec buffers
+    for (i = 0; i < buf->count; i++) {
+        free(buf->vecs[i].buf);
+    }
+    
+    // free the vector list
+    free(buf->vecs);
+}
+
+/**
+ * transport_methods::read implementation.
+ */
+static err_t transport_test__read (transport_t *transport, void *buf_ptr, size_t *len, error_t *err)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+    struct io_buf *buf = &tp->recv_buf;
+    struct io_vec *vec = buf->read_vec;
+    
+    // EOF/nonblock if we're past the end of the last vector
+    if (!vec || vec == buf->vecs + buf->count || buf->off >= vec->len) {
+        if (!tp->eof) {
+            // wait for more to be fed in
+            *len = 0;
+            return SUCCESS;
+
+        } else {
+            // EOF!
+            return SET_ERROR(err, ERR_EOF);
+        }
+    }
+    
+    // amount of data available in this iovec
+    size_t available = vec->len - buf->off;
+
+    // amount to read
+    size_t to_read = *len;
+    
+    // trim down?
+    if (to_read > available)
+        to_read = available;
+
+    // copy
+    memcpy(buf_ptr, vec->buf + buf->off, to_read);
+    
+
+    if (to_read < available) {
+        // bytes still left in the vector
+        buf->off += to_read;
+
+    } else {
+        // consumed the whole vector
+        // XXX: release data?
+        buf->read_vec++;
+        buf->off = 0;
+    }
+
+    // update len
+    *len = to_read;
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * transport_methods::write implementation.
+ */
+static err_t transport_test__write (transport_t *transport, const void *data, size_t *len, error_t *err)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+
+    // write it out
+    // XXX: partial writes?
+    if ((ERROR_CODE(err) = io_buf_write(&tp->send_buf, data, *len)))
+        goto error;
+    
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);
+}
+
+static err_t transport_test__events (transport_t *transport, short mask, error_t *err)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+
+    (void) tp;
+    (void) mask;
+    (void) err;
+    
+    // XXX: don't re-trigger anything
+    
+    return SUCCESS;
+}
+
+static void transport_test__deinit (transport_t *transport)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+
+    transport_test_destroy(tp);
+}
+
+/*
+ * Our sock_stream_type
+ */
+const struct transport_type transport_test_type = {
+    .base_type = {
+        .parent     = &transport_type_type,
+    },
+    .methods                = {
+        .read       = transport_test__read,
+        .write      = transport_test__write,
+        .events     = transport_test__events,
+        .deinit     = transport_test__deinit
+    },
+};
+
+struct transport_test* transport_test_create (struct transport_info *info)
+{
+    struct transport_test *tp;
+
+    // allocate
+    assert((tp = calloc(1, sizeof(*tp))));
+    
+    // initialize base with our transport_type
+    transport_init(TRANSPORT_TEST_BASE(tp), &transport_test_type, info);
+
+    // ok
+    return tp;
+}
+
+transport_t* transport_test_cast (struct transport_test *tp)
+{
+    return TRANSPORT_TEST_BASE(tp);
+}
+
+void transport_test_event (struct transport_test *tp, short what)
+{
+    // invoke, masking out as needed
+    // this won't do anything if all the bits are masked out
+    transport_invoke(TRANSPORT_TEST_BASE(tp), what & TRANSPORT_TEST_BASE(tp)->info.ev_mask);
+}
+
+void transport_test_push_buf (struct transport_test *tp, const char *data, size_t len)
+{
+    // push it
+    assert(io_buf_write(&tp->recv_buf, data, len) == SUCCESS);
+    
+    // notify
+    transport_test_event(tp, TRANSPORT_READ);
+}
+
+void transport_test_push_str (struct transport_test *tp, const char *str)
+{
+    // push it
+    transport_test_push_buf(tp, str, strlen(str));
+}
+
+void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...)
+{
+    char buf[TRANSPORT_TEST_FMT_MAX];
+    size_t ret;
+
+    // format
+    va_list vargs; va_start(vargs, fmt);
+    assert((ret = vsnprintf(buf, sizeof(buf), fmt, vargs)) <= sizeof(buf));
+    va_end(vargs);
+       
+    // push it
+    transport_test_push_buf(tp, buf, ret);
+}
+
+void transport_test_push_eof (struct transport_test *tp)
+{
+    // update state
+    tp->eof = true;
+
+    transport_test_event(tp, TRANSPORT_READ);
+}
+
+void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr)
+{
+    struct io_buf *buf = &tp->send_buf;
+    size_t len = 0, i, off = 0;
+    char *out;
+    
+    // calculate total size
+    for (i = 0; i < buf->count; i++) {
+        len += buf->vecs[i].len;
+    }
+
+    // alloc
+    assert((out = malloc(len)));
+
+    // copy
+    for (i = 0; i < buf->count; i++) {
+        struct io_vec *vec = buf->vecs + i;
+
+        memcpy(out + off, vec->buf, vec->len);
+        off += vec->len;
+        
+        // zero
+        free(vec->buf); vec->buf = NULL;
+        vec->len = 0;
+    }
+    
+    // update return
+    *buf_ptr = out;
+    *len_ptr = len;
+
+    // update write_vec
+    buf->write_vec = buf->vecs;
+}
+
+void transport_test_async_error (struct transport_test *tp, const error_t *err)
+{
+    transport_error(&tp->base, err);
+}
+
+void transport_test_destroy (struct transport_test *tp)
+{
+    // free the buffers
+    io_buf_destroy(&tp->send_buf);
+    io_buf_destroy(&tp->recv_buf);
+}
+