src/transport_test.c
branchnew-transport
changeset 166 cb8cb023cf06
parent 139 55b9dcc2b73a
child 182 471ca1e744da
equal deleted inserted replaced
165:b3e95108c884 166:cb8cb023cf06
       
     1 #include "transport_test.h"
       
     2 #include "transport_internal.h"
       
     3 
       
     4 #include <stdlib.h>
       
     5 #include <string.h>
       
     6 #include <stdio.h>
       
     7 #include <stdarg.h>
       
     8 #include <assert.h>
       
     9 
       
    10 /**
       
    11  * Simple IO vector
       
    12  */
       
    13 struct io_vec {
       
    14     /** The buffer */
       
    15     char *buf;
       
    16 
       
    17     /** Buffer size */
       
    18     size_t len;
       
    19 };
       
    20 
       
    21 /**
       
    22  * Simple vectored IO-buffer
       
    23  */
       
    24 struct io_buf {
       
    25     /** The array of buffer-vectors, {NULL}-terminated */
       
    26     struct io_vec *vecs;
       
    27 
       
    28     /** The number of io_vecs */
       
    29     size_t count;
       
    30 
       
    31     /** Current read/write vector */
       
    32     struct io_vec *read_vec, *write_vec;
       
    33 
       
    34     /** Offset into current vector */
       
    35     size_t off;
       
    36 };
       
    37 
       
    38 /**
       
    39  * Forward-declare our transport_type
       
    40  */
       
    41 extern const struct transport_type transport_test_type;
       
    42 
       
    43 
       
    44 /**
       
    45  * A dummy sock_stream implementation intended for testing purposes.
       
    46  */
       
    47 struct transport_test {
       
    48     /** The base transport stuff */
       
    49     struct transport base;
       
    50 
       
    51     /** The send/recieve buffers */
       
    52     struct io_buf send_buf, recv_buf;
       
    53 
       
    54     /** No more data is going to be added, return EOF once all the rest is consumed */
       
    55     bool eof;
       
    56 };
       
    57 
       
    58 /**
       
    59  * Get a transport pointer from a transport_test pointer
       
    60  */
       
    61 #define TRANSPORT_TEST_BASE(tp_ptr) (&(tp_ptr)->base)
       
    62 
       
    63 /**
       
    64  * Grow buf->vecs if needed to ensure that buf->write_vec points to a valid io_vec
       
    65  */
       
    66 static err_t io_buf_grow (struct io_buf *buf)
       
    67 {
       
    68     size_t read_vec_offset = buf->read_vec ? (buf->read_vec - buf->vecs) : 0;
       
    69     size_t write_vec_offset = buf->write_vec ? (buf->write_vec - buf->vecs) : 0;
       
    70     struct io_vec *v;
       
    71     struct io_vec *vecs_tmp = buf->vecs;
       
    72 
       
    73     // don't grow if not full
       
    74     if (buf->vecs && buf->write_vec < buf->vecs + buf->count)
       
    75         return SUCCESS;
       
    76 
       
    77     // new size
       
    78     buf->count = buf->count * 2 + 1;
       
    79 
       
    80     // grow
       
    81     if ((buf->vecs = realloc(buf->vecs, buf->count * sizeof(struct io_vec))) == NULL) {
       
    82         // restore old value
       
    83         buf->vecs = vecs_tmp;
       
    84 
       
    85         return ERR_CALLOC;
       
    86     }
       
    87 
       
    88     // restore vec positions
       
    89     buf->write_vec = buf->vecs + write_vec_offset;
       
    90     buf->read_vec = buf->vecs + read_vec_offset;
       
    91 
       
    92     // zero new vecs
       
    93     for (v = buf->write_vec; v < buf->vecs + buf->count; v++)
       
    94         memset(v, 0, sizeof(*v));
       
    95 
       
    96     // ok
       
    97     return SUCCESS;
       
    98 }
       
    99 
       
   100 /**
       
   101  * Write some data to an io_buf, copying it.
       
   102  */
       
   103 static err_t io_buf_write (struct io_buf *buf, const char *data, size_t len)
       
   104 {
       
   105     error_t err;
       
   106 
       
   107     // ensure there's room
       
   108     if ((ERROR_CODE(&err) = io_buf_grow(buf)))
       
   109         goto error;
       
   110     
       
   111     // the vector to use
       
   112     struct io_vec *vec = buf->write_vec;
       
   113 
       
   114     // allocate
       
   115     if ((vec->buf = malloc(len)) == NULL)
       
   116         JUMP_SET_ERROR(&err, ERR_MEM);
       
   117     
       
   118     // store
       
   119     vec->len = len;
       
   120     memcpy(vec->buf, data, len);
       
   121 
       
   122     // vec consumed
       
   123     buf->write_vec++;
       
   124 
       
   125     // ok
       
   126     return SUCCESS;
       
   127 
       
   128 error:
       
   129     return ERROR_CODE(&err);
       
   130 }
       
   131 
       
   132 /**
       
   133  * Destroy the io_buf, freeing all resources.
       
   134  *
       
   135  * The io_buf must not be used anymore.
       
   136  */
       
   137 static void io_buf_destroy (struct io_buf *buf)
       
   138 {
       
   139     size_t i;
       
   140 
       
   141     // free the io_vec buffers
       
   142     for (i = 0; i < buf->count; i++) {
       
   143         free(buf->vecs[i].buf);
       
   144     }
       
   145     
       
   146     // free the vector list
       
   147     free(buf->vecs);
       
   148 }
       
   149 
       
   150 /**
       
   151  * transport_methods::read implementation.
       
   152  */
       
   153 static err_t transport_test_read (transport_t *transport, void *buf_ptr, size_t *len, error_t *err)
       
   154 {
       
   155     struct transport_test *tp = transport_check(transport, &transport_test_type);
       
   156     struct io_buf *buf = &tp->recv_buf;
       
   157     struct io_vec *vec = buf->read_vec;
       
   158     
       
   159     // EOF/nonblock if we're past the end of the last vector
       
   160     if (!vec || vec == buf->vecs + buf->count || buf->off >= vec->len) {
       
   161         if (!tp->eof) {
       
   162             // wait for more to be fed in
       
   163             *len = 0;
       
   164             return SUCCESS;
       
   165 
       
   166         } else {
       
   167             // EOF!
       
   168             return SET_ERROR(err, ERR_EOF);
       
   169         }
       
   170     }
       
   171     
       
   172     // amount of data available in this iovec
       
   173     size_t available = vec->len - buf->off;
       
   174 
       
   175     // amount to read
       
   176     size_t to_read = *len;
       
   177     
       
   178     // trim down?
       
   179     if (to_read > available)
       
   180         to_read = available;
       
   181 
       
   182     // copy
       
   183     memcpy(buf_ptr, vec->buf + buf->off, to_read);
       
   184     
       
   185 
       
   186     if (to_read < available) {
       
   187         // bytes still left in the vector
       
   188         buf->off += to_read;
       
   189 
       
   190     } else {
       
   191         // consumed the whole vector
       
   192         // XXX: release data?
       
   193         buf->read_vec++;
       
   194         buf->off = 0;
       
   195     }
       
   196 
       
   197     // update len
       
   198     *len = to_read;
       
   199 
       
   200     // ok
       
   201     return SUCCESS;
       
   202 }
       
   203 
       
   204 /**
       
   205  * transport_methods::write implementation.
       
   206  */
       
   207 static err_t transport_test_write (transport_t *transport, const void *data, size_t *len, error_t *err)
       
   208 {
       
   209     struct transport_test *tp = transport_check(transport, &transport_test_type);
       
   210 
       
   211     // write it out
       
   212     // XXX: partial writes?
       
   213     if ((ERROR_CODE(err) = io_buf_write(&tp->send_buf, data, *len)))
       
   214         goto error;
       
   215     
       
   216     // ok
       
   217     return SUCCESS;
       
   218 
       
   219 error:
       
   220     return ERROR_CODE(err);
       
   221 }
       
   222 
       
   223 static err_t transport_test_events (transport_t *transport, short mask, error_t *err)
       
   224 {
       
   225     struct transport_test *tp = transport_check(transport, &transport_test_type);
       
   226 
       
   227     (void) tp;
       
   228     (void) mask;
       
   229     (void) err;
       
   230     
       
   231     // XXX: don't re-trigger anything
       
   232     
       
   233     return SUCCESS;
       
   234 }
       
   235 
       
   236 static void _transport_test_destroy (transport_t *transport)
       
   237 {
       
   238     struct transport_test *tp = transport_check(transport, &transport_test_type);
       
   239 
       
   240     transport_test_destroy(tp);
       
   241 }
       
   242 
       
   243 /*
       
   244  * Our sock_stream_type
       
   245  */
       
   246 const struct transport_type transport_test_type = {
       
   247     .methods                = {
       
   248         .read               = transport_test_read,
       
   249         .write              = transport_test_write,
       
   250         .events             = transport_test_events,
       
   251         .destroy            = _transport_test_destroy
       
   252     },
       
   253 };
       
   254 
       
   255 struct transport_test* transport_test_create (struct transport_info *info)
       
   256 {
       
   257     struct transport_test *tp;
       
   258 
       
   259     // allocate
       
   260     assert((tp = calloc(1, sizeof(*tp))));
       
   261     
       
   262     // initialize base with our transport_type
       
   263     transport_init(TRANSPORT_TEST_BASE(tp), &transport_test_type, info);
       
   264 
       
   265     // ok
       
   266     return tp;
       
   267 }
       
   268 
       
   269 transport_t* transport_test_cast (struct transport_test *tp)
       
   270 {
       
   271     return TRANSPORT_TEST_BASE(tp);
       
   272 }
       
   273 
       
   274 void transport_test_event (struct transport_test *tp, short what)
       
   275 {
       
   276     // invoke, masking out as needed
       
   277     // this won't do anything if all the bits are masked out
       
   278     transport_invoke(TRANSPORT_TEST_BASE(tp), what & TRANSPORT_TEST_BASE(tp)->info.ev_mask);
       
   279 }
       
   280 
       
   281 void transport_test_push_buf (struct transport_test *tp, const char *data, size_t len)
       
   282 {
       
   283     // push it
       
   284     assert(io_buf_write(&tp->recv_buf, data, len) == SUCCESS);
       
   285     
       
   286     // notify
       
   287     transport_test_event(tp, TRANSPORT_READ);
       
   288 }
       
   289 
       
   290 void transport_test_push_str (struct transport_test *tp, const char *str)
       
   291 {
       
   292     // push it
       
   293     transport_test_push_buf(tp, str, strlen(str));
       
   294 }
       
   295 
       
   296 void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...)
       
   297 {
       
   298     char buf[TRANSPORT_TEST_FMT_MAX];
       
   299     size_t ret;
       
   300 
       
   301     // format
       
   302     va_list vargs; va_start(vargs, fmt);
       
   303     assert((ret = vsnprintf(buf, sizeof(buf), fmt, vargs)) <= sizeof(buf));
       
   304     va_end(vargs);
       
   305        
       
   306     // push it
       
   307     transport_test_push_buf(tp, buf, ret);
       
   308 }
       
   309 
       
   310 void transport_test_push_eof (struct transport_test *tp)
       
   311 {
       
   312     // update state
       
   313     tp->eof = true;
       
   314 
       
   315     transport_test_event(tp, TRANSPORT_READ);
       
   316 }
       
   317 
       
   318 void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr)
       
   319 {
       
   320     struct io_buf *buf = &tp->send_buf;
       
   321     size_t len = 0, i, off = 0;
       
   322     char *out;
       
   323     
       
   324     // calculate total size
       
   325     for (i = 0; i < buf->count; i++) {
       
   326         len += buf->vecs[i].len;
       
   327     }
       
   328 
       
   329     // alloc
       
   330     assert((out = malloc(len)));
       
   331 
       
   332     // copy
       
   333     for (i = 0; i < buf->count; i++) {
       
   334         struct io_vec *vec = buf->vecs + i;
       
   335 
       
   336         memcpy(out + off, vec->buf, vec->len);
       
   337         off += vec->len;
       
   338         
       
   339         // zero
       
   340         free(vec->buf); vec->buf = NULL;
       
   341         vec->len = 0;
       
   342     }
       
   343     
       
   344     // update return
       
   345     *buf_ptr = out;
       
   346     *len_ptr = len;
       
   347 
       
   348     // update write_vec
       
   349     buf->write_vec = buf->vecs;
       
   350 }
       
   351 
       
   352 void transport_test_destroy (struct transport_test *tp)
       
   353 {
       
   354     // free the buffers
       
   355     io_buf_destroy(&tp->send_buf);
       
   356     io_buf_destroy(&tp->recv_buf);
       
   357 }
       
   358