ccn_seqwriter.c

Go to the documentation of this file.
00001 /**
00002  * @file ccn_seqwriter.c
00003  * @brief
00004  * 
00005  * Part of the CCNx C Library.
00006  *
00007  * Copyright (C) 2010-2011 Palo Alto Research Center, Inc.
00008  *
00009  * This library is free software; you can redistribute it and/or modify it
00010  * under the terms of the GNU Lesser General Public License version 2.1
00011  * as published by the Free Software Foundation.
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details. You should have received
00016  * a copy of the GNU Lesser General Public License along with this library;
00017  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00018  * Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 
00021 #include <stddef.h>
00022 #include <stdint.h>
00023 #include <stdlib.h>
00024 #include <errno.h>
00025 #include <ccn/ccn.h>
00026 #include <ccn/seqwriter.h>
00027 
00028 #define MAX_DATA_SIZE 4096
00029 
00030 struct ccn_seqwriter {
00031     struct ccn_closure cl;
00032     struct ccn *h;
00033     struct ccn_charbuf *nb;
00034     struct ccn_charbuf *nv;
00035     struct ccn_charbuf *buffer;
00036     struct ccn_charbuf *cob0;
00037     uintmax_t seqnum;
00038     int batching;
00039     int blockminsize;
00040     int blockmaxsize;
00041     unsigned char interests_possibly_pending;
00042     unsigned char closed;
00043 };
00044 
00045 static struct ccn_charbuf *
00046 seqw_next_cob(struct ccn_seqwriter *w)
00047 {
00048     struct ccn_charbuf *cob = ccn_charbuf_create();
00049     struct ccn_charbuf *name = ccn_charbuf_create();
00050     struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT;
00051     int res;
00052     
00053     if (w->closed)
00054         sp.sp_flags |= CCN_SP_FINAL_BLOCK;
00055     ccn_charbuf_append(name, w->nv->buf, w->nv->length);
00056     ccn_name_append_numeric(name, CCN_MARKER_SEQNUM, w->seqnum);
00057     res = ccn_sign_content(w->h, cob, name, &sp, w->buffer->buf, w->buffer->length);
00058     if (res < 0)
00059         ccn_charbuf_destroy(&cob);
00060     ccn_charbuf_destroy(&name);
00061     return(cob);
00062 }
00063 
00064 static enum ccn_upcall_res
00065 seqw_incoming_interest(
00066                        struct ccn_closure *selfp,
00067                        enum ccn_upcall_kind kind,
00068                        struct ccn_upcall_info *info)
00069 {
00070     int res;
00071     struct ccn_charbuf *cob = NULL;
00072     struct ccn_seqwriter *w = selfp->data;
00073     
00074     if (w == NULL || selfp != &(w->cl))
00075         abort();
00076     switch (kind) {
00077         case CCN_UPCALL_FINAL:
00078             ccn_charbuf_destroy(&w->nb);
00079             ccn_charbuf_destroy(&w->nv);
00080             ccn_charbuf_destroy(&w->buffer);
00081             ccn_charbuf_destroy(&w->cob0);
00082             free(w);
00083             break;
00084         case CCN_UPCALL_INTEREST:
00085             if (w->closed || w->buffer->length > w->blockminsize) {
00086                 cob = seqw_next_cob(w);
00087                 if (cob == NULL)
00088                     return(CCN_UPCALL_RESULT_OK);
00089                 if (ccn_content_matches_interest(cob->buf, cob->length,
00090                                                  1, NULL,
00091                                                  info->interest_ccnb,
00092                                                  info->pi->offset[CCN_PI_E],
00093                                                  info->pi)) {
00094                     w->interests_possibly_pending = 0;
00095                     res = ccn_put(info->h, cob->buf, cob->length);
00096                     if (res >= 0) {
00097                         w->buffer->length = 0;
00098                         w->seqnum++;
00099                         return(CCN_UPCALL_RESULT_INTEREST_CONSUMED);
00100                     }
00101                 }
00102                 ccn_charbuf_destroy(&cob);
00103             }
00104             if (w->cob0 != NULL) {
00105                 cob = w->cob0;
00106                 if (ccn_content_matches_interest(cob->buf, cob->length,
00107                                                  1, NULL,
00108                                                  info->interest_ccnb,
00109                                                  info->pi->offset[CCN_PI_E],
00110                                                  info->pi)) {
00111                     w->interests_possibly_pending = 0;
00112                     ccn_put(info->h, cob->buf, cob->length);
00113                     return(CCN_UPCALL_RESULT_INTEREST_CONSUMED);
00114                 }
00115             }
00116             w->interests_possibly_pending = 1;
00117             break;
00118         default:
00119             break;
00120     }
00121     return(CCN_UPCALL_RESULT_OK);
00122 }
00123 
00124 /**
00125  * Create a seqwriter for writing data to a versioned, segmented stream.
00126  *
00127  * @param name is a ccnb-encoded Name.  It will be provided with a version
00128  *        based on the current time unless it already ends in a version
00129  *        component.
00130  */
00131 struct ccn_seqwriter *
00132 ccn_seqw_create(struct ccn *h, struct ccn_charbuf *name)
00133 {
00134     struct ccn_seqwriter *w = NULL;
00135     struct ccn_charbuf *nb = NULL;
00136     struct ccn_charbuf *nv = NULL;
00137     int res;
00138     
00139     w = calloc(1, sizeof(*w));
00140     if (w == NULL)
00141         return(NULL);
00142     nb = ccn_charbuf_create();
00143     ccn_charbuf_append(nb, name->buf, name->length);
00144     nv = ccn_charbuf_create();
00145     ccn_charbuf_append(nv, name->buf, name->length);
00146     res = ccn_create_version(h, nv, CCN_V_NOW, 0, 0);
00147     if (res < 0 || nb == NULL) {
00148         ccn_charbuf_destroy(&nv);
00149         ccn_charbuf_destroy(&nb);
00150         free(w);
00151         return(NULL);
00152     }
00153     
00154     w->cl.p = &seqw_incoming_interest;
00155     w->cl.data = w;
00156     w->nb = nb;
00157     w->nv = nv;
00158     w->buffer = ccn_charbuf_create();
00159     w->h = h;
00160     w->seqnum = 0;
00161     w->interests_possibly_pending = 1;
00162     w->blockminsize = 0;
00163     w->blockmaxsize = MAX_DATA_SIZE;
00164     res = ccn_set_interest_filter(h, nb, &(w->cl));
00165     if (res < 0) {
00166         ccn_charbuf_destroy(&w->nb);
00167         ccn_charbuf_destroy(&w->nv);
00168         ccn_charbuf_destroy(&w->buffer);
00169         free(w);
00170         return(NULL);
00171     }
00172     return(w);
00173 }
00174 
00175 /**
00176  * Append to a charbuf the versioned ccnb-encoded Name that will be used for
00177  * this stream.
00178  *
00179  * @param w the seqwriter for which the name is requested
00180  * @param nv the charbuf to which the name will be appended
00181  * @returns 0 for success, -1 for failure
00182  */
00183 int
00184 ccn_seqw_get_name(struct ccn_seqwriter *w, struct ccn_charbuf *nv)
00185 {
00186     if (nv == NULL || w == NULL)
00187         return (-1);
00188     return (ccn_charbuf_append_charbuf(nv, w->nv));
00189 }
00190 
00191 /**
00192  * Write some data to a seqwriter.
00193  *
00194  * This is roughly analogous to a write(2) call in non-blocking mode.
00195  *
00196  * The current implementation returns an error and refuses the new data if
00197  * it does not fit in the current buffer.
00198  * That is, there are no partial writes.
00199  * In this case, the caller should ccn_run() for a little while and retry.
00200  * 
00201  * It is also an error to attempt to write more than 4096 bytes.
00202  *
00203  * @returns the size written, or -1 for an error.  In case of an error,
00204  *          the caller may test ccn_geterror() for values of EAGAIN or
00205  *          EINVAL from errno.h.
00206  */
00207 int
00208 ccn_seqw_write(struct ccn_seqwriter *w, const void *buf, size_t size)
00209 {
00210     struct ccn_charbuf *cob = NULL;
00211     int res;
00212     int ans;
00213     
00214     if (w == NULL || w->cl.data != w)
00215         return(-1);
00216     if (w->buffer == NULL || size > w->blockmaxsize)
00217         return(ccn_seterror(w->h, EINVAL));
00218     ans = size;
00219     if (size + w->buffer->length > w->blockmaxsize)
00220         ans = ccn_seterror(w->h, EAGAIN);
00221     else if (size != 0)
00222         ccn_charbuf_append(w->buffer, buf, size);
00223     if (w->interests_possibly_pending &&
00224         (w->closed || w->buffer->length >= w->blockminsize) &&
00225         (w->batching == 0 || ans == -1)) {
00226         cob = seqw_next_cob(w);
00227         if (cob != NULL) {
00228             res = ccn_put(w->h, cob->buf, cob->length);
00229             if (res >= 0) {
00230                 if (w->seqnum == 0) {
00231                     w->cob0 = cob;
00232                     cob = NULL;
00233                 }
00234                 w->buffer->length = 0;
00235                 w->seqnum++;
00236                 w->interests_possibly_pending = 0;
00237             }
00238             ccn_charbuf_destroy(&cob);
00239         }
00240     }
00241     return(ans);
00242 }
00243 
00244 /**
00245  * Start a batch of writes.
00246  *
00247  * This will delay the signing of content objects until the batch ends,
00248  * producing a more efficient result.
00249  * Must have a matching ccn_seqw_batch_end() call.
00250  * Batching may be nested.
00251  */
00252 int
00253 ccn_seqw_batch_start(struct ccn_seqwriter *w)
00254 {
00255     if (w == NULL || w->cl.data != w || w->closed)
00256         return(-1);
00257     return(++(w->batching));
00258 }
00259 
00260 /**
00261  * End a batch of writes.
00262  */
00263 int
00264 ccn_seqw_batch_end(struct ccn_seqwriter *w)
00265 {
00266     if (w == NULL || w->cl.data != w || w->batching == 0)
00267         return(-1);
00268     if (--(w->batching) == 0)
00269         ccn_seqw_write(w, NULL, 0);
00270     return(w->batching);
00271 }
00272 int
00273 ccn_seqw_set_block_limits(struct ccn_seqwriter *w, int l, int h)
00274 {
00275     if (w == NULL || w->cl.data != w || w->closed)
00276         return(-1);
00277     if (l < 0 || l > MAX_DATA_SIZE || h < 0 || h > MAX_DATA_SIZE || l > h)
00278         return(-1);
00279     w->blockminsize = l;
00280     w->blockmaxsize = h;
00281     return(0);
00282 }
00283 /**
00284  * Assert that an interest has possibly been expressed that matches
00285  * the seqwriter's data.  This is useful, for example, if the seqwriter
00286  * was created in response to an interest.
00287  */
00288 int
00289 ccn_seqw_possible_interest(struct ccn_seqwriter *w)
00290 {
00291     if (w == NULL || w->cl.data != w)
00292         return(-1);
00293     w->interests_possibly_pending = 1;
00294     ccn_seqw_write(w, NULL, 0);
00295     return(0);
00296 }
00297 
00298 /**
00299  * Close the seqwriter, which will be freed.
00300  */
00301 int
00302 ccn_seqw_close(struct ccn_seqwriter *w)
00303 {
00304     if (w == NULL || w->cl.data != w)
00305         return(-1);
00306     w->closed = 1;
00307     w->interests_possibly_pending = 1;
00308     w->batching = 0;
00309     ccn_seqw_write(w, NULL, 0);
00310     ccn_set_interest_filter(w->h, w->nb, NULL);
00311     return(0);
00312 }

Generated on Thu Feb 16 00:44:00 2012 for Content-Centric Networking in C by  doxygen 1.5.6