# HG changeset patch # User Tero Marttila # Date 1241360345 -10800 # Node ID cb8cb023cf06db11deced2096baf868468a291e0 # Parent b3e95108c8849a0a94b6acde08f18a6c9f100d18 replace old sock_test with new transport_test diff -r b3e95108c884 -r cb8cb023cf06 src/sock_test.c --- a/src/sock_test.c Sun May 03 17:18:16 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,288 +0,0 @@ -#include "sock_test.h" - -#include -#include -#include - -/** - * Grow buf->vecs as needed, to ensure that buf->write_vec is valid - */ -static err_t sock_test_grow_buf (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; - } - - // set vec - 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++) { - v->buf = NULL; - v->len = 0; - } - - // ok - return SUCCESS; -} - -static err_t sock_test_read (struct sock_stream *base_sock, void *buf_ptr, size_t *len, struct error_info *err) -{ - struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); - struct io_buf *buf = &sock->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 (sock->nonblocking && !sock->eof) { - // wait for more to be fed in - *len = 0; - return SUCCESS; - - } else { - // EOF! - return SET_ERROR(err, ERR_READ_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); - - // consumed the whole vec? - if (to_read < available) { - // move offset - buf->off += to_read; - - } else { - // next vector - buf->read_vec++; - } - - // update len - *len = to_read; - - // ok - return SUCCESS; -} - -static err_t sock_test_write (struct sock_stream *base_sock, const void *buf_ptr, size_t *len, struct error_info *err) -{ - struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); - struct io_buf *buf = &sock->send_buf; - - // ensure there's room - if ((ERROR_CODE(err) = sock_test_grow_buf(buf))) - goto error; - - // the next buffer - struct io_vec *vec = buf->write_vec; - - // store - vec->len = *len; - assert((vec->buf = malloc(vec->len))); - memcpy(vec->buf, buf_ptr, vec->len); - - // move vec onwards - buf->write_vec++; - - // ok - return SUCCESS; - -error: - return ERROR_CODE(err); -} - -static err_t sock_test_event_init (struct sock_stream *base_sock) -{ - struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); - - // set the nonblocking flag - sock->nonblocking = true; - - return SUCCESS; -} - -static err_t sock_test_event_enable (struct sock_stream *base_sock, short mask) -{ - struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); - - // store mask - sock->ev_mask = mask; - - return SUCCESS; -} - -static void sock_test_release (struct sock_stream *base_sock) -{ - struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); - - sock_test_destroy(sock); -} - -/* - * Our sock_stream_type - */ -static struct sock_stream_type sock_test_type = { - .methods = { - .read = &sock_test_read, - .write = &sock_test_write, - .event_init = &sock_test_event_init, - .event_enable = &sock_test_event_enable, - .release = &sock_test_release, - }, -}; - -struct sock_test* sock_test_create (void) -{ - struct sock_test *sock; - - // allocate - assert((sock = calloc(1, sizeof(*sock)))); - - // initialize base with our sock_stream_type - sock_stream_init(SOCK_TEST_BASE(sock), &sock_test_type, NULL, NULL); - - // ok - return sock; -} - -void sock_test_destroy (struct sock_test *sock) -{ - size_t i; - struct io_buf *sbuf = &sock->send_buf, *rbuf = &sock->recv_buf; - - // free the send buffer - for (i = 0; i < sbuf->count; i++) { - free(sbuf->vecs[i].buf); - } - - // free the buffer vector lists - free(sbuf->vecs); - free(rbuf->vecs); - - // free the sock itself - free(sock); -} - -void sock_test_set_recv_buffer (struct sock_test *sock, struct io_vec *vecs, size_t count, bool eof) -{ - struct io_buf *buf = &sock->recv_buf; - - // allocate + copy - assert((buf->vecs = calloc(count, sizeof(struct io_vec)))); - memcpy(buf->vecs, vecs, count * sizeof(struct io_vec)); - - // set - buf->count = count; - buf->read_vec = buf->vecs; - buf->write_vec = buf->vecs + count; - buf->off = 0; - - // set EOF flag? - if (eof) - sock->eof = true; -} - -void sock_test_notify_events (struct sock_test *sock) -{ - // notify if events are enabled - if (sock->ev_mask) { - // zero mask - int mask = sock->ev_mask; - sock->ev_mask = 0; - - sock_stream_invoke_callbacks(SOCK_TEST_BASE(sock), mask); - } -} - -void sock_test_add_recv_vec (struct sock_test *sock, struct io_vec new_vec) -{ - struct io_buf *buf = &sock->recv_buf; - - // ensure there's room - assert(sock_test_grow_buf(buf) == SUCCESS); - - // copy - *(buf->write_vec++) = new_vec; - - // notify - sock_test_notify_events(sock); -} - -void sock_test_add_recv_str (struct sock_test *sock, const char *str) -{ - struct io_vec vec = { - (char*) str, strlen(str) - }; - - sock_test_add_recv_vec(sock, vec); -} - -void sock_test_set_recv_eof (struct sock_test *sock) -{ - sock->eof = true; - - sock_test_notify_events(sock); -} - -void sock_test_get_send_data (struct sock_test *sock, char **buf_ptr, size_t *len_ptr) -{ - struct io_buf *buf = &sock->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; -} - diff -r b3e95108c884 -r cb8cb023cf06 src/sock_test.h --- a/src/sock_test.h Sun May 03 17:18:16 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -#ifndef SOCK_TEST_H -#define SOCK_TEST_H - -/** - * @file - * - * Dummy sock_stream implemention for local testing. - */ -#include "sock_internal.h" -#include - -/** - * 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; -}; - -/** - * A dummy sock_stream implementation intended for testing purposes. - */ -struct sock_test { - /** The base struct for sock_stream_* functions */ - struct sock_stream base; - - /** The send/recieve buffers */ - struct io_buf send_buf, recv_buf; - - /** non-blocking mode? */ - bool nonblocking; - - /** No more data is going to be added, return EOF once all the rest is consumed */ - bool eof; - - /** event flags */ - int ev_mask; -}; - -/** - * Get a sock_stream pointer from a sock_tcp pointer - */ -#define SOCK_TEST_BASE(sock_ptr) (&(sock_ptr)->base) - -/** - * Get the sock_stream.err pointer from a sock_tcp pointer - */ -#define SOCK_TEST_ERR(sock_ptr) SOCK_ERR(SOCK_TEST_BASE(sock_ptr)) - -/** - * A dummy stream socket intended for testing purposes. - */ -struct sock_test* sock_test_create (void); - -/** - * Destroy the sock buffer, releasing any resource we allocated ourself - */ -void sock_test_destroy (struct sock_test *sock); - -/** - * Set the recieve buffer contents. - * - * The vectors themselves are copied, but the data they contain is not. - * - * If the EOF flag is given, it indicates that no more data will be added, otherwise the eof status is unchanged. - */ -void sock_test_set_recv_buffer (struct sock_test *sock, struct io_vec *vecs, size_t count, bool eof); - -/** - * Add some data to the recieve buffer. - * - * If events are enabled, they are triggered. - */ -void sock_test_add_recv_vec (struct sock_test *sock, struct io_vec vec); - -/** - * Add a string to the recieve buffer using sock_test_add_recv_vec() - */ -void sock_test_add_recv_str (struct sock_test *sock, const char *str); - -/** - * Set EOF on recv, and trigger events. - */ -void sock_test_set_recv_eof (struct sock_test *sock); - -/** - * Get the send buffer contents as a single string, free() after use if you care about that. - * - * Clears the send buffer, so this doesn't return the same data twice. - */ -void sock_test_get_send_data (struct sock_test *sock, char **buf, size_t *len); - -#endif diff -r b3e95108c884 -r cb8cb023cf06 src/transport_test.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/transport_test.c Sun May 03 17:19:05 2009 +0300 @@ -0,0 +1,358 @@ +#include "transport_test.h" +#include "transport_internal.h" + +#include +#include +#include +#include +#include + +/** + * 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_destroy (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 = { + .methods = { + .read = transport_test_read, + .write = transport_test_write, + .events = transport_test_events, + .destroy = _transport_test_destroy + }, +}; + +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_destroy (struct transport_test *tp) +{ + // free the buffers + io_buf_destroy(&tp->send_buf); + io_buf_destroy(&tp->recv_buf); +} + diff -r b3e95108c884 -r cb8cb023cf06 src/transport_test.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/transport_test.h Sun May 03 17:19:05 2009 +0300 @@ -0,0 +1,74 @@ +#ifndef TRANSPORT_TEST_H +#define TRANSPORT_TEST_H + +/** + * @file + * + * Dummy transport implemention for local testing. + */ +#include "transport.h" + +/** + * The opaque transport state + */ +struct transport_test; + +/** + * Construct a new, empty, connected transport_test. + */ +struct transport_test* transport_test_create (struct transport_info *info); + +/** + * A transport_test is a valid transport, this performs the cast + */ +transport_t* transport_test_cast (struct transport_test *tp); + +/** + * Invoke the transport's user callbacks for the given event mask, unless masked out. + */ +void transport_test_event (struct transport_test *tp, short what); + +/** + * Adds a data buffer to the recieve buffer. + * + * The given data is copied. + * + * If events are enabled, they are triggered. + */ +void transport_test_push_buf (struct transport_test *tp, const char *buf, size_t len); + +/** + * Add a string to the recieve buffer using transport_test_push_buf() + */ +void transport_test_push_str (struct transport_test *tp, const char *str); + +/** + * Maximum length of a formatted string pushed + */ +#define TRANSPORT_TEST_FMT_MAX 4096 + +/** + * Add a formatted string to the recieve buffer + * + * @see TRANSPORT_TEST_FMT_MAX + */ +void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...); + +/** + * Set EOF on recv. + */ +void transport_test_push_eof (struct transport_test *tp); + +/** + * Get the send buffer contents as a single buffer, free() after use. + * + * This clears the send buffer, so this doesn't return the same data twice. + */ +void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr); + +/** + * Destroy the transport buffer, releasing any buffers we allocated ourself + */ +void transport_test_destroy (struct transport_test *tp); + +#endif /* TRANSPORT_TEST_H */