src/lib/transport_test.c
branchnew-lib-errors
changeset 219 cefec18b8268
parent 196 873796250c60
equal deleted inserted replaced
218:5229a5d098b2 219:cefec18b8268
       
     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__deinit (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     .base_type = {
       
   248         .parent     = &transport_type_type,
       
   249     },
       
   250     .methods                = {
       
   251         .read       = transport_test__read,
       
   252         .write      = transport_test__write,
       
   253         .events     = transport_test__events,
       
   254         .deinit     = transport_test__deinit
       
   255     },
       
   256 };
       
   257 
       
   258 struct transport_test* transport_test_create (struct transport_info *info)
       
   259 {
       
   260     struct transport_test *tp;
       
   261 
       
   262     // allocate
       
   263     assert((tp = calloc(1, sizeof(*tp))));
       
   264     
       
   265     // initialize base with our transport_type
       
   266     transport_init(TRANSPORT_TEST_BASE(tp), &transport_test_type, info);
       
   267 
       
   268     // ok
       
   269     return tp;
       
   270 }
       
   271 
       
   272 transport_t* transport_test_cast (struct transport_test *tp)
       
   273 {
       
   274     return TRANSPORT_TEST_BASE(tp);
       
   275 }
       
   276 
       
   277 void transport_test_event (struct transport_test *tp, short what)
       
   278 {
       
   279     // invoke, masking out as needed
       
   280     // this won't do anything if all the bits are masked out
       
   281     transport_invoke(TRANSPORT_TEST_BASE(tp), what & TRANSPORT_TEST_BASE(tp)->info.ev_mask);
       
   282 }
       
   283 
       
   284 void transport_test_push_buf (struct transport_test *tp, const char *data, size_t len)
       
   285 {
       
   286     // push it
       
   287     assert(io_buf_write(&tp->recv_buf, data, len) == SUCCESS);
       
   288     
       
   289     // notify
       
   290     transport_test_event(tp, TRANSPORT_READ);
       
   291 }
       
   292 
       
   293 void transport_test_push_str (struct transport_test *tp, const char *str)
       
   294 {
       
   295     // push it
       
   296     transport_test_push_buf(tp, str, strlen(str));
       
   297 }
       
   298 
       
   299 void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...)
       
   300 {
       
   301     char buf[TRANSPORT_TEST_FMT_MAX];
       
   302     size_t ret;
       
   303 
       
   304     // format
       
   305     va_list vargs; va_start(vargs, fmt);
       
   306     assert((ret = vsnprintf(buf, sizeof(buf), fmt, vargs)) <= sizeof(buf));
       
   307     va_end(vargs);
       
   308        
       
   309     // push it
       
   310     transport_test_push_buf(tp, buf, ret);
       
   311 }
       
   312 
       
   313 void transport_test_push_eof (struct transport_test *tp)
       
   314 {
       
   315     // update state
       
   316     tp->eof = true;
       
   317 
       
   318     transport_test_event(tp, TRANSPORT_READ);
       
   319 }
       
   320 
       
   321 void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr)
       
   322 {
       
   323     struct io_buf *buf = &tp->send_buf;
       
   324     size_t len = 0, i, off = 0;
       
   325     char *out;
       
   326     
       
   327     // calculate total size
       
   328     for (i = 0; i < buf->count; i++) {
       
   329         len += buf->vecs[i].len;
       
   330     }
       
   331 
       
   332     // alloc
       
   333     assert((out = malloc(len)));
       
   334 
       
   335     // copy
       
   336     for (i = 0; i < buf->count; i++) {
       
   337         struct io_vec *vec = buf->vecs + i;
       
   338 
       
   339         memcpy(out + off, vec->buf, vec->len);
       
   340         off += vec->len;
       
   341         
       
   342         // zero
       
   343         free(vec->buf); vec->buf = NULL;
       
   344         vec->len = 0;
       
   345     }
       
   346     
       
   347     // update return
       
   348     *buf_ptr = out;
       
   349     *len_ptr = len;
       
   350 
       
   351     // update write_vec
       
   352     buf->write_vec = buf->vecs;
       
   353 }
       
   354 
       
   355 void transport_test_async_error (struct transport_test *tp, const error_t *err)
       
   356 {
       
   357     transport_error(&tp->base, err);
       
   358 }
       
   359 
       
   360 void transport_test_destroy (struct transport_test *tp)
       
   361 {
       
   362     // free the buffers
       
   363     io_buf_destroy(&tp->send_buf);
       
   364     io_buf_destroy(&tp->recv_buf);
       
   365 }
       
   366