src/sock_test.c
author Tero Marttila <terom@fixme.fi>
Thu, 12 Mar 2009 21:12:48 +0200
changeset 41 40f7aa051acb
parent 40 51678c7eae03
child 42 13cfc41f76a7
permissions -rw-r--r--
add line_proto test, enhance others
#include "sock_test.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

/**
 * 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 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(SOCK_TEST_ERR(sock), 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 sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test);
    struct io_buf *buf = &sock->send_buf;
    
    // ensure there's room
    assert(sock_test_grow_buf(buf) == SUCCESS);
    
    // 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;
}

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);

    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);

    // 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_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;
}

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++) {
        memcpy(out + off, buf->vecs[i].buf, buf->vecs[i].len);
        off += buf->vecs[i].len;
    }
    
    // update return
    *buf_ptr = out;
    *len_ptr = len;
}