ccn_fetch.c

Go to the documentation of this file.
00001 /*
00002  * lib/ccn_fetch.c
00003  * 
00004  * Part of the CCNx C Library.
00005  *
00006  * Copyright (C) 2010-2011 Palo Alto Research Center, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or modify it
00009  * under the terms of the GNU Lesser General Public License version 2.1
00010  * as published by the Free Software Foundation.
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014  * Lesser General Public License for more details. You should have received
00015  * a copy of the GNU Lesser General Public License along with this library;
00016  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00017  * Fifth Floor, Boston, MA 02110-1301 USA.
00018  */
00019 
00020 /**
00021  * Streaming access for fetching segmented CCNx data.
00022  *
00023  * Supports multiple streams from a single connection and
00024  * seeking to an arbitrary position within the associated file.
00025  *
00026  * TBD: need to fix up the case where a segment cannot be fetched but we are
00027  * not really at the end of the stream data.  This case can occur if we express
00028  * an interest for a segment and the interest times out.  Current behavior is
00029  * to treat this as an end-of-stream (prematurely and silently)
00030  *
00031  * TBD: need to provide a more principled (or maybe just controlled) way to
00032  * handle interest timeouts.
00033  */
00034 
00035 #include <ccn/fetch.h>
00036 
00037 #include <sys/types.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <errno.h>
00041 #include <string.h>
00042 #include <time.h>
00043 #include <unistd.h>
00044 
00045 #include <sys/time.h>
00046 
00047 // TBD: the following constants should be more principled
00048 #define CCN_VERSION_TIMEOUT 8000
00049 #define CCN_INTEREST_TIMEOUT_USECS 15000000
00050 #define MaxSuffixDefault 4
00051 
00052 typedef intmax_t seg_t;
00053 
00054 typedef uint64_t TimeMarker;
00055 
00056 static TimeMarker
00057 GetCurrentTimeUSecs(void) {
00058         const TimeMarker M = 1000*1000;
00059         struct timeval now = {0};
00060     gettimeofday(&now, 0);
00061         return now.tv_sec*M+now.tv_usec;
00062 }
00063 
00064 static intmax_t
00065 DeltaTime(TimeMarker mt1, TimeMarker mt2) {
00066         return(mt2-mt1);
00067 }
00068 
00069 ///////////////////////////////////////////////////////
00070 
00071 struct ccn_fetch {
00072         struct ccn *h;
00073         FILE *debug;
00074         ccn_fetch_flags debugFlags;
00075         int localConnect;
00076         int nStreams;
00077         int maxStreams;
00078         struct ccn_fetch_stream **streams;
00079 };
00080 
00081 struct ccn_fetch_buffer {
00082         struct ccn_fetch_buffer *next;
00083         seg_t seg;                      // the seg for this buffer (< 0 if unassigned)
00084         intmax_t pos;           // the base byte position for this segment
00085         int len;                        // the number of valid bytes
00086         int max;                        // the buffer size
00087         unsigned char *buf;     // where the bytes are
00088 };
00089 
00090 struct localClosure {
00091         struct ccn_fetch_stream *fs;
00092         struct localClosure *next;
00093         seg_t reqSeg;
00094         TimeMarker startClock;
00095 };
00096 
00097 struct ccn_fetch_stream {
00098         struct ccn_fetch *parent;
00099         struct localClosure *requests;  // segment requests in process
00100         int reqBusy;                    // the number of requests busy
00101         int maxBufs;                    // max number of buffers allowed
00102         int nBufs;                              // the number of buffers allocated
00103         struct ccn_fetch_buffer *bufList;       // the buffer list
00104         char *id;
00105         struct ccn_charbuf *name;                       // interest name (without seq#)
00106         struct ccn_charbuf *interest;           // interest template
00107         int segSize;                    // the segment size (-1 if variable, 0 if unknown)
00108         int segsAhead;
00109         intmax_t fileSize;              // the file size (< 0 if unassigned)
00110         intmax_t readPosition;  // the read position (always assigned)
00111         intmax_t readStart;             // the read position at segment start
00112         seg_t readSeg;                  // the segment for the readPosition
00113         seg_t timeoutSeg;               // the lowest timeout segment seen
00114         seg_t zeroLenSeg;               // the lowest zero len segment seen
00115         seg_t finalSeg;                 // final segment number (< 0 if not known yet)
00116         int finalSegLen;                // final segment length
00117         intmax_t timeoutUSecs;  // microseconds for interest timeout
00118         intmax_t timeoutsSeen;
00119         seg_t segsRead;
00120         seg_t segsRequested;
00121 };
00122 
00123 // forward reference
00124 static enum ccn_upcall_res
00125 CallMe(struct ccn_closure *selfp,
00126            enum ccn_upcall_kind kind,
00127            struct ccn_upcall_info *info);
00128 
00129 ///////////////////////////////////////////////////////
00130 // Internal routines
00131 ///////////////////////////////////////////////////////
00132 
00133 static char *globalNullString = "";
00134 static char *
00135 newStringCopy(const char *src) {
00136         int n = ((src == NULL) ? 0 : strlen(src));
00137         if (n <= 0 || src == globalNullString) return globalNullString;
00138         char *s = calloc(n+1, sizeof(*s));
00139         strncpy(s, src, n);
00140         return s;
00141 }
00142 
00143 static char *
00144 freeString(char * s) {
00145         if (s != NULL && s != globalNullString)
00146                 free(s);
00147         return NULL;
00148 }
00149 
00150 static struct ccn_charbuf *
00151 sequenced_name(struct ccn_charbuf *basename, seg_t seq) {
00152     // creates a new struct ccn_charbuf *, appending the sequence number to the basename
00153         struct ccn_charbuf *name = ccn_charbuf_create();
00154     ccn_charbuf_append_charbuf(name, basename);
00155         if (seq >= 0)
00156                 ccn_name_append_numeric(name, CCN_MARKER_SEQNUM, seq);
00157     return(name);
00158 }
00159 
00160 static struct ccn_charbuf *
00161 make_data_template(int maxSuffix) {
00162         // creates a template for interests that only have a name
00163         // and a segment number
00164         struct ccn_charbuf *cb = ccn_charbuf_create();
00165     ccn_charbuf_append_tt(cb, CCN_DTAG_Interest, CCN_DTAG);
00166     ccn_charbuf_append_tt(cb, CCN_DTAG_Name, CCN_DTAG);
00167     ccn_charbuf_append_closer(cb); /* </Name> */
00168     ccn_charbuf_append_tt(cb, CCN_DTAG_MaxSuffixComponents, CCN_DTAG);
00169     ccnb_append_number(cb, maxSuffix);
00170     ccn_charbuf_append_closer(cb); /* </MaxSuffixComponents> */
00171     ccn_charbuf_append_closer(cb); /* </Interest> */
00172     return(cb);
00173 }
00174 
00175 static seg_t
00176 GetNumberFromInfo(const unsigned char *ccnb,
00177                                   enum ccn_dtag tt, size_t start, size_t stop) {
00178         // gets the binary number for the info
00179         // based on the tag and the start and stop indexes
00180         // returns -1 if the number does not appear to exist
00181         // must be called from inside of CallMe
00182         if (start < stop) {
00183                 size_t len = 0;
00184                 const unsigned char *data = NULL;
00185                 ccn_ref_tagged_BLOB(tt, ccnb, start, stop, &data, &len);
00186                 if (len > 0 && data != NULL) {
00187                         // parse big-endian encoded number
00188                         seg_t n = 0;
00189                         size_t i;
00190             for (i = 0; i < len; i++) {
00191                                 n = n * 256 + data[i];
00192                         }
00193                         return n;
00194                 }
00195         }
00196         return -1;
00197 }
00198 
00199 static seg_t
00200 GetFinalSegment(struct ccn_upcall_info *info) {
00201         // gets the final segment number for the content
00202         // returns -1 if it is not yet known
00203         // must be called from inside of CallMe
00204         if (info == NULL) return -1;
00205         const unsigned char *ccnb = info->content_ccnb;
00206         if (ccnb == NULL || info->pco == NULL) return -1;
00207         int start = info->pco->offset[CCN_PCO_B_FinalBlockID];
00208         int stop = info->pco->offset[CCN_PCO_E_FinalBlockID];
00209         return GetNumberFromInfo(ccnb, CCN_DTAG_FinalBlockID, start, stop);
00210 }
00211 
00212 static struct localClosure *
00213 AddSegRequest(struct ccn_fetch_stream *fs, seg_t seg) {
00214         // adds a segment request, returns NULL if already present
00215         // or if the seg given is outside the valid range
00216         // returns the new request if it was created
00217         FILE *debug = fs->parent->debug;
00218         ccn_fetch_flags flags = fs->parent->debugFlags;
00219         if (seg < 0) return NULL;
00220         if (fs->finalSeg >= 0 && seg > fs->finalSeg) return NULL;
00221         struct localClosure *req = fs->requests;
00222         while (req != NULL) {
00223                 if (req->reqSeg == seg) return NULL;
00224                 req = req->next;
00225         }
00226         req = calloc(1, sizeof(*req));
00227         req->fs = fs;
00228         req->reqSeg = seg;
00229         req->startClock = GetCurrentTimeUSecs();
00230         req->next = fs->requests;
00231         fs->requests = req;
00232         if (debug != NULL && (flags & ccn_fetch_flags_NoteAddRem)) {
00233                 fprintf(debug, "-- ccn_fetch AddSegRequest %s, seg %jd\n",
00234                                 fs->id, seg);
00235                 fflush(debug);
00236         }
00237         return req;
00238 }
00239 
00240 static struct localClosure *
00241 RemSegRequest(struct ccn_fetch_stream *fs, struct localClosure *req) {
00242         // removes a segment request
00243         // returns NULL if the request was removed
00244         // if not found then just returns the request
00245         FILE *debug = fs->parent->debug;
00246         ccn_fetch_flags flags = fs->parent->debugFlags;
00247         struct localClosure *this = fs->requests;
00248         struct localClosure *lag = NULL;
00249         seg_t seg = req->reqSeg;
00250         while (this != NULL) {
00251                 struct localClosure *next = this->next;
00252                 if (this == req) {
00253                         if (lag == NULL) {
00254                                 fs->requests = next;
00255                         } else {
00256                                 lag->next = next;
00257                         }
00258                         req->fs = NULL;
00259                         if (debug != NULL && (flags & ccn_fetch_flags_NoteAddRem)) {
00260                                 fprintf(debug, "-- ccn_fetch RemSegRequest %s, seg %jd\n",
00261                                                 fs->id, seg);
00262                                 fflush(debug);
00263                         }
00264                         return NULL;
00265                 }
00266                 lag = this;
00267                 this = next;
00268         }
00269         if (debug != NULL && (flags & ccn_fetch_flags_NoteAddRem)) {
00270                 fprintf(debug, "-- ccn_fetch RemSegRequest %s, seg %jd, NOT FOUND!\n",
00271                                 fs->id, seg);
00272                 fflush(debug);
00273         }
00274         return req;
00275 }
00276 
00277 static struct ccn_fetch_buffer *
00278 FindBufferForSeg(struct ccn_fetch_stream *fs, seg_t seg) {
00279         // finds the buffer object given the seg
00280         struct ccn_fetch_buffer *fb = fs->bufList;
00281         for (;;) {
00282                 if (fb == NULL) break;
00283                 if (fb->seg == seg) break;
00284                 fb = fb->next;
00285         } 
00286         return fb;
00287 }
00288 
00289 static struct ccn_fetch_buffer *
00290 FindBufferForPosition(struct ccn_fetch_stream *fs, intmax_t pos) {
00291         // finds the buffer object given the seg
00292         struct ccn_fetch_buffer *fb = fs->bufList;
00293         for (;;) {
00294                 if (fb == NULL) break;
00295                 intmax_t fp = fb->pos;
00296                 if (fp >= 0 && pos >= fp && pos < fp+fb->len) break;
00297                 fb = fb->next;
00298         } 
00299         return fb;
00300 }
00301 
00302 static intmax_t
00303 InferPosition(struct ccn_fetch_stream *fs, seg_t seg) {
00304         intmax_t pos = -1; // by default, size is unknown
00305         if (seg == 0) {
00306                 // initial seg is easy, size regardless
00307                 pos = 0;
00308         } else if (fs->segSize > 0) {
00309                 // fixed size is almost as easy
00310                 pos = seg*fs->segSize;
00311         } else if (seg == fs->readSeg) {
00312                 // position is based on the current read position 
00313                 pos = fs->readStart;
00314         } else {
00315                 // try to get the position from the previous buffer
00316                 struct ccn_fetch_buffer *ofb = FindBufferForSeg(fs, seg-1);
00317                 if (ofb != NULL && ofb->pos >= 0)
00318                         pos = ofb->pos+ofb->len;
00319         }
00320         return pos;
00321 }
00322 
00323 static struct ccn_fetch_buffer *
00324 NewBufferForSeg(struct ccn_fetch_stream *fs, seg_t seg, size_t len) {
00325         // makes a new buffer for the segment
00326         struct ccn_fetch_buffer *fb = calloc(1, sizeof(*fb));
00327         if (len > 0) fb->buf = calloc(len, sizeof(unsigned char));
00328         fb->seg = seg;
00329         intmax_t pos = InferPosition(fs, seg);
00330         fb->pos = pos;
00331         fb->len = len;
00332         fs->nBufs++;
00333         fb->next = fs->bufList;
00334         fs->bufList = fb;
00335         fs->segsAhead++;
00336         if (fs->segsAhead >= fs->maxBufs) fs->segsAhead = fs->maxBufs-1;
00337         if (fs->segSize <= 0 && pos >= 0) {
00338                 // segment size is variable or unknown
00339                 // position for buffer is known, so propagate forwards
00340                 for (;;) {
00341                         if (fs->fileSize < 0) {
00342                                 // maybe we just found the file size
00343                                 if (seg == fs->finalSeg
00344                                         || (seg+1 == fs->finalSeg && fs->finalSegLen == 0))
00345                                         fs->fileSize = pos+len;
00346                         }
00347                         seg++;
00348                         struct ccn_fetch_buffer *ofb = FindBufferForSeg(fs, seg);
00349                         if (ofb == NULL || ofb->pos >= 0) break;
00350                         pos = pos + len;
00351                         ofb->pos = pos;
00352                         len = ofb->len;
00353                 }
00354         }
00355         return fb;
00356 }
00357 
00358 static void
00359 PruneSegments(struct ccn_fetch_stream *fs) {
00360         intmax_t start = fs->readStart;
00361         struct ccn_fetch_buffer *lag = NULL;
00362         struct ccn_fetch_buffer *fb = fs->bufList;
00363         while (fb != NULL && fs->nBufs > fs->maxBufs) {
00364                 struct ccn_fetch_buffer *next = fb->next;
00365                 if (fs->maxBufs == 0 || (fb->pos >= 0 && start > (fb->pos + fb->len))) {
00366                         // this buffer is going away
00367                         // note: keep buffer immediately before readStart if possible
00368                         if (lag == NULL) {
00369                                 fs->bufList = next;
00370                         } else {
00371                                 lag->next = next;
00372                         }
00373                         if (fb->buf != NULL) free(fb->buf);
00374                         free(fb);
00375                         fs->nBufs--;
00376                 } else {
00377                         // keep this buffer in play
00378                         lag = fb;
00379                 }
00380                 fb = next;
00381         }
00382 }
00383 
00384 static void
00385 NeedSegment(struct ccn_fetch_stream *fs, seg_t seg) {
00386         // requests that a specific segment interest be registered
00387         // but ONLY if it the request not already in flight
00388         // AND the segment is not already in a buffer
00389         struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, seg);
00390         if (fb != NULL)
00391                 // no point in requesting what we have
00392                 return;
00393         if (fs->finalSeg >= 0 && seg > fs->finalSeg)
00394                 // no point in requesting off the end, either
00395                 return;
00396         if (fs->timeoutSeg > 0 && seg >= fs->timeoutSeg)
00397                 // don't request a timed-out segment
00398                 return;
00399         if (fs->zeroLenSeg > 0 && seg >= fs->zeroLenSeg)
00400                 // don't request a zero-length segment
00401                 return;
00402         struct localClosure *req = AddSegRequest(fs, seg);
00403         if (req != NULL) {
00404                 FILE *debug = fs->parent->debug;
00405                 ccn_fetch_flags flags = fs->parent->debugFlags;
00406                 struct ccn_charbuf *temp = sequenced_name(fs->name, seg);
00407                 struct ccn *h = fs->parent->h;
00408                 struct ccn_closure *action = calloc(1, sizeof(*action));
00409                 action->data = req;
00410                 action->p = &CallMe;
00411                 int res = ccn_express_interest(h, temp, action, fs->interest);
00412                 ccn_charbuf_destroy(&temp);
00413                 if (res >= 0) {
00414                         // the ccn connection accepted our request
00415                         fs->reqBusy++;
00416                         fs->segsRequested++;
00417                         if (debug != NULL && (flags & ccn_fetch_flags_NoteNeed)) {
00418                                 fprintf(debug,
00419                                                 "-- ccn_fetch NeedSegment %s, seg %jd",
00420                                                 fs->id, seg);
00421                                 if (fs->finalSeg >= 0)
00422                                         fprintf(debug, ", final %jd", fs->finalSeg);
00423                                 fprintf(debug, "\n");
00424                                 fflush(debug);
00425                         }
00426                         return;
00427                 }
00428                 // the request was not placed, so get rid of the evidence
00429                 // CallMe won't get a chance to free it
00430                 if (debug != NULL && (flags & ccn_fetch_flags_NoteNeed)) {
00431                         fprintf(debug,
00432                                         "** ccn_fetch NeedSegment failed, %s, seg %jd\n",
00433                                         fs->id, seg);
00434                         fflush(debug);
00435                 }
00436                 RemSegRequest(fs, req);
00437                 free(req);
00438                 free(action);
00439         
00440         }
00441 }
00442 
00443 static void
00444 NeedSegments(struct ccn_fetch_stream *fs) {
00445         // determines which segments should be requested
00446         // based on the current readSeg and maxBufs 
00447         seg_t loSeg = fs->readSeg;
00448         seg_t hiSeg = loSeg+fs->segsAhead;
00449         seg_t finalSeg = fs->finalSeg;
00450         if (finalSeg >= 0 && hiSeg > finalSeg) hiSeg = finalSeg;
00451         if (loSeg > hiSeg) hiSeg = loSeg;
00452         while (loSeg <= hiSeg) {
00453                 // try to request needed segments
00454                 NeedSegment(fs, loSeg);
00455                 loSeg++;
00456         }
00457 }
00458 
00459 static void
00460 ShowDelta(FILE *f, TimeMarker from) {
00461         intmax_t dt = DeltaTime(from, GetCurrentTimeUSecs());
00462         fprintf(f, ", dt %jd.%06d\n", dt / 1000000, (int) (dt % 1000000));
00463         fflush(f);
00464 }
00465 
00466 static enum ccn_upcall_res
00467 CallMe(struct ccn_closure *selfp,
00468            enum ccn_upcall_kind kind,
00469            struct ccn_upcall_info *info) {
00470         // CallMe is the callback routine invoked by ccn_run when a registered
00471         // interest has something interesting happen.
00472     struct localClosure *req = (struct localClosure *)selfp->data;
00473         seg_t thisSeg = req->reqSeg;
00474     struct ccn_fetch_stream *fs = (struct ccn_fetch_stream *) req->fs;
00475         if (fs == NULL) {
00476                 if (kind == CCN_UPCALL_FINAL) {
00477                         // orphaned, so just get rid of it
00478                         free(req);
00479                         free(selfp);
00480                 }
00481                 return(CCN_UPCALL_RESULT_OK);
00482         }
00483         FILE *debug = fs->parent->debug;
00484         seg_t finalSeg = fs->finalSeg;
00485         ccn_fetch_flags flags = fs->parent->debugFlags;
00486         if (finalSeg < 0) {
00487                 // worth a try to find the last segment
00488                 finalSeg = GetFinalSegment(info);
00489                 fs->finalSeg = finalSeg;
00490         }
00491     
00492         switch (kind) {
00493                 case CCN_UPCALL_FINAL:
00494                         // this is the cleanup for an expressed interest
00495                         req = RemSegRequest(fs, req);
00496                         if (fs->reqBusy > 0) fs->reqBusy--;
00497                         free(selfp);
00498                         return(CCN_UPCALL_RESULT_OK);
00499                 case CCN_UPCALL_INTEREST_TIMED_OUT: {
00500                         if (finalSeg >= 0 && thisSeg > finalSeg)
00501                                 // ignore this timeout quickly
00502                                 return(CCN_UPCALL_RESULT_OK);
00503                         intmax_t dt = DeltaTime(req->startClock, GetCurrentTimeUSecs());
00504                         if (dt >= fs->timeoutUSecs) {
00505                                 // timed out, too many retries
00506                                 // assume that this interest will never produce
00507                                 seg_t timeoutSeg = fs->timeoutSeg;
00508                                 fs->timeoutsSeen++;
00509                                 fs->segsAhead = 0;
00510                                 if (timeoutSeg < 0 || thisSeg < timeoutSeg) {
00511                                         // we can infer a new timeoutSeg
00512                                         fs->timeoutSeg = thisSeg;
00513                                 }
00514                                 if (debug != NULL && (flags & ccn_fetch_flags_NoteTimeout)) {
00515                                         fprintf(debug, 
00516                                                         "** ccn_fetch timeout, %s, seg %jd",
00517                                                         fs->id, thisSeg);
00518                                         fprintf(debug, 
00519                                                         ", dt %jd us, timeoutUSecs %jd\n",
00520                                                         dt, fs->timeoutUSecs);
00521                                         fflush(debug);
00522                                 }
00523                                 return(CCN_UPCALL_RESULT_OK);
00524                         }
00525                         // TBD: may need to reseed bloom filter?  who to ask?
00526                         return(CCN_UPCALL_RESULT_REEXPRESS);
00527                 }
00528                 case CCN_UPCALL_CONTENT_UNVERIFIED:
00529                         return (CCN_UPCALL_RESULT_VERIFY);
00530                 case CCN_UPCALL_CONTENT_KEYMISSING:
00531                         return (CCN_UPCALL_RESULT_FETCHKEY);
00532                 case CCN_UPCALL_CONTENT:
00533                 case CCN_UPCALL_CONTENT_RAW:
00534                         if (fs->timeoutSeg >= 0 && fs->timeoutSeg <= thisSeg)
00535                                 // we will ignore this, since we are blocked
00536                                 return(CCN_UPCALL_RESULT_OK);
00537                         break;
00538                 default:
00539                         // SHOULD NOT HAPPEN
00540                         return(CCN_UPCALL_RESULT_ERR);
00541     }
00542         
00543         struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, thisSeg);
00544         if (fb == NULL) {
00545                 // we don't already have the data yet
00546                 const unsigned char *data = NULL;
00547                 size_t dataLen = 0;
00548                 size_t ccnb_size = info->pco->offset[CCN_PCO_E];
00549                 const unsigned char *ccnb = info->content_ccnb;
00550                 int res = ccn_content_get_value(ccnb, ccnb_size, info->pco,
00551                                                                                 &data, &dataLen);
00552                 
00553                 if (res < 0 || (thisSeg != finalSeg && dataLen == 0)) {
00554                         // we got a bogus result, no data in this content!
00555                         if (debug != NULL && (flags & ccn_fetch_flags_NoteAddRem)) {
00556                                 fprintf(debug, 
00557                                                 "-- ccn_fetch no data, %s, seg %jd, final %jd",
00558                                                 fs->id, thisSeg, finalSeg);
00559                                 ShowDelta(debug, req->startClock);
00560                         }
00561                         if (fs->zeroLenSeg < 0 || thisSeg < fs->zeroLenSeg)
00562                                 // note this problem for future reporting
00563                                 fs->zeroLenSeg = thisSeg;
00564                 } else if (thisSeg == finalSeg && dataLen == 0) {
00565                         // EOF, but no buffer needed
00566                         if (fs->fileSize < 0)
00567                                 fs->fileSize = InferPosition(fs, thisSeg);
00568                         fs->finalSeg = finalSeg-1;
00569                         if (debug != NULL && (flags & ccn_fetch_flags_NoteFinal)) {
00570                                 fprintf(debug, 
00571                                                 "-- ccn_fetch EOF, %s, seg %jd, len %d, fs %jd",
00572                                                 fs->id, thisSeg,
00573                                                 (int) dataLen,
00574                                                 fs->fileSize);
00575                                 ShowDelta(debug, req->startClock);
00576                         }
00577                         
00578                 } else {
00579                         // alloc a buffer and transfer the data
00580                         
00581                         if (fs->segSize == 0) {
00582                                 // assuming fixed size segments, so any should do
00583                                 // EXCEPT for an incomplete final segment
00584                                 if (thisSeg == 0 || thisSeg < finalSeg)
00585                                         fs->segSize = dataLen;
00586                         }
00587                         if (thisSeg == finalSeg) fs->finalSegLen = dataLen;
00588                         struct ccn_fetch_buffer *fb = NewBufferForSeg(fs, thisSeg, dataLen);
00589                         memcpy(fb->buf, data, dataLen);
00590                         if (debug != NULL && (flags & ccn_fetch_flags_NoteFill)) {
00591                                 fprintf(debug, 
00592                                                 "-- ccn_fetch FillSeg, %s, seg %jd, len %d, nbuf %d",
00593                                                 fs->id, thisSeg, (int) dataLen, (int) fs->nBufs);
00594                                 ShowDelta(debug, req->startClock);
00595                         }
00596                         if (thisSeg == finalSeg) {
00597                                 // the file size is known in segments
00598                                 if (fs->segSize <= 0) {
00599                                         // variable or unknown segment size
00600                                         if (fb->pos >= 0) {
00601                                                 fs->fileSize = fb->pos + dataLen;
00602                                         }
00603                                 } else {
00604                                         // fixed segment size, so file size is now known
00605                                         fs->fileSize = thisSeg * fs->segSize + dataLen;
00606                                 }
00607                                 if (debug != NULL && (flags & ccn_fetch_flags_NoteFinal)) {
00608                                         fprintf(debug, 
00609                                                         "-- ccn_fetch EOF, %s, seg %jd, len %d, fs %jd",
00610                                                         fs->id, thisSeg, (int) dataLen, fs->fileSize);
00611                                         ShowDelta(debug, req->startClock);
00612                                 }
00613                         }
00614                         fs->segsRead++;
00615                 }
00616         }
00617         
00618         ccn_set_run_timeout(fs->parent->h, 0);
00619         return(CCN_UPCALL_RESULT_OK);
00620 }
00621 
00622 ///////////////////////////////////////////////////////
00623 // External routines
00624 ///////////////////////////////////////////////////////
00625 
00626 /**
00627  * Creates a new ccn_fetch object using the given ccn connection.
00628  * If h == NULL, attempts to create a new connection automatically.
00629  * @returns NULL if the creation was not successful
00630  * (only can happen for the h == NULL case).
00631  */
00632 extern struct ccn_fetch *
00633 ccn_fetch_new(struct ccn *h) {
00634         struct ccn_fetch *f = calloc(1, sizeof(*f));
00635         if (h == NULL) {
00636                 h = ccn_create();
00637                 int connRes = ccn_connect(h, NULL);
00638                 if (connRes < 0) {
00639                         ccn_destroy(&h);
00640                         free(f);
00641                         return NULL;
00642                 }
00643                 f->localConnect = 1;
00644         }
00645         f->h = h;
00646         return f;
00647 }
00648 
00649 void
00650 ccn_fetch_set_debug(struct ccn_fetch *f, FILE *debug, ccn_fetch_flags flags) {
00651         f->debug = debug;
00652         f->debugFlags = flags;
00653 }
00654 
00655 /**
00656  * Destroys a ccn_fetch object.
00657  * Only destroys the underlying ccn connection if it was automatically created.
00658  * Forces all underlying streams to close immediately.
00659  * @returns NULL in all cases.
00660  */
00661 extern struct ccn_fetch *
00662 ccn_fetch_destroy(struct ccn_fetch *f) {
00663         // destroys a ccn_fetch object
00664         // always returns NULL
00665         // only destroys the underlying ccn connection if it was
00666         // automatically created, otherwise does not alter it
00667         if (f != NULL) {
00668                 struct ccn *h = f->h;
00669                 if (h != NULL && f->localConnect) {
00670                         ccn_disconnect(h);
00671                         ccn_destroy(&f->h);
00672                 }
00673                 // take down all of the streams
00674                 while (f->nStreams > 0) {
00675                         struct ccn_fetch_stream *fs = f->streams[0];
00676                         if (fs == NULL) break;
00677                         ccn_fetch_close(fs);
00678                 }
00679                 free(f);
00680         }
00681         return NULL;
00682 }
00683 
00684 /**
00685  * Polls the underlying streams and attempts to make progress.
00686  * Scans the streams for those that have data already present, or are at the end
00687  * of the stream.  If the count is 0, perfoms a ccn_poll on the underlying
00688  * ccn connection with a 0 timeout.
00689  *
00690  * NOTE: periodic calls to ccn_fetch_poll should be performed to update
00691  * the contents of the streams UNLESS the client is calling ccn_run for
00692  * the underlying ccn connection.
00693  * @returns the count of streams that have pending data or have ended.
00694  */
00695 extern int
00696 ccn_fetch_poll(struct ccn_fetch *f) {
00697         int i;
00698     int count = 0;
00699         int ns = f->nStreams;
00700         for (i = 0; i < ns; i++) {
00701                 struct ccn_fetch_stream *fs = f->streams[i];
00702                 if (fs != NULL) {
00703                         intmax_t avail = ccn_fetch_avail(fs);
00704                         if (avail >= 0) count++;
00705                 }
00706         }
00707         // we should try for more progress
00708         ccn_run(f->h, 0);
00709         return count;
00710 }
00711 
00712 /**
00713  * Provides an iterator through the underlying streams.
00714  * Use fs == NULL to start the iteration, and an existing stream to continue
00715  * the iteration.
00716  * @returns the next stream in the iteration, or NULL at the end.
00717  * Note that providing a stale (closed) stream handle will return NULL.
00718  */
00719 extern struct ccn_fetch_stream *
00720 ccn_fetch_next(struct ccn_fetch *f, struct ccn_fetch_stream *fs) {
00721         int i;
00722     int ns = f->nStreams;
00723     struct ccn_fetch_stream *lag = NULL;
00724         for (i = 0; i < ns; i++) {
00725                 struct ccn_fetch_stream *tfs = f->streams[i];
00726                 if (tfs != NULL) {
00727                         if (lag == fs) return tfs;
00728                         lag = tfs;
00729                 }
00730         }
00731         return NULL;
00732 }
00733 
00734 /**
00735  * @returns the underlying ccn connection.
00736  */
00737 extern struct ccn *
00738 ccn_fetch_get_ccn(struct ccn_fetch *f) {
00739         return f->h;
00740 }
00741 
00742 /**
00743  * Creates a stream for a named interest.
00744  * The name should be a ccnb encoded interest.
00745  * If resolveVersion, then we assume that the version is unresolved, 
00746  * and an attempt is made to determine the version number using the highest
00747  * version.
00748  * The number of buffers (nBufs) may be silently limited.
00749  * @returns NULL if the stream creation failed,
00750  * otherwise returns the new stream.
00751  */
00752 extern struct ccn_fetch_stream *
00753 ccn_fetch_open(struct ccn_fetch *f,
00754                            struct ccn_charbuf *name,
00755                            const char *id,
00756                            struct ccn_charbuf *interestTemplate,
00757                            int maxBufs,
00758                            int resolveVersion,
00759                            int assumeFixed) {
00760         // returns a new ccn_fetch_stream object based on the arguments
00761         // returns NULL if not successful
00762     if (maxBufs <= 0) return NULL;
00763         if (maxBufs > 16) maxBufs = 16;
00764         int res = 0;
00765         FILE *debug = f->debug;
00766         ccn_fetch_flags flags = f->debugFlags;
00767     
00768         // first, resolve the version
00769         struct ccn_fetch_stream *fs = calloc(1, sizeof(*fs));
00770         fs->segSize = (assumeFixed ? 0 : -1);
00771         fs->name = ccn_charbuf_create();
00772         fs->id = newStringCopy(id);
00773         ccn_charbuf_append_charbuf(fs->name, name);
00774         if (resolveVersion) {
00775                 int tmInc = 40; // TBD: need better strategy for version timeout
00776                 int tm = 0;
00777                 while (tm < CCN_VERSION_TIMEOUT) {
00778                         res = ccn_resolve_version(f->h, fs->name, resolveVersion, tmInc);
00779                         if (res >= 0) break;
00780                         tm = tm + tmInc;
00781                 }
00782                 if (res < 0) {
00783                         // could not resolve version for this name
00784                         // get rid of allocations so far and bail out
00785                         if (debug != NULL && (flags & ccn_fetch_flags_NoteOpenClose)) {
00786                                 fprintf(debug, 
00787                                                 "-- ccn_fetch open, %s, failed to resolve version\n",
00788                                                 fs->id);
00789                                 fflush(debug);
00790                         }
00791                         ccn_charbuf_destroy(&fs->name);
00792                         freeString(fs->id);
00793                         free(fs);
00794                         return NULL;
00795                 }
00796         }
00797         fs->maxBufs = maxBufs;
00798         fs->segsAhead = 0;
00799         fs->fileSize = -1;
00800         fs->finalSeg = -1;
00801         fs->timeoutSeg = -1;
00802         fs->zeroLenSeg = -1;
00803         fs->parent = f;
00804         fs->timeoutUSecs = CCN_INTEREST_TIMEOUT_USECS;  // TBD: how to get better timeout?
00805         
00806         // use the supplied template or the default
00807         if (interestTemplate != NULL) {
00808                 struct ccn_charbuf *cb = ccn_charbuf_create();
00809                 ccn_charbuf_append_charbuf(cb, interestTemplate);
00810                 fs->interest = cb;
00811         } else
00812                 fs->interest = make_data_template(MaxSuffixDefault);
00813         
00814         
00815         // remember the stream in the parent
00816         int ns = f->nStreams;
00817         int max = f->maxStreams;
00818         if (ns >= max) {
00819                 // extend the vector
00820                 int nMax = max+max/2+4;
00821         f->streams = realloc(f->streams, sizeof(*(f->streams)) * nMax);
00822                 f->maxStreams = nMax;
00823         }
00824         // guaranteed room to add at the end
00825         f->streams[ns] = fs;
00826         f->nStreams = ns+1;
00827         
00828         if (debug != NULL && (flags & ccn_fetch_flags_NoteOpenClose)) {
00829                 fprintf(debug, 
00830                                 "-- ccn_fetch open, %s\n",
00831                                 fs->id);
00832                 fflush(debug);
00833         }
00834         // prep for the first segment
00835         NeedSegment(fs, 0);
00836         return fs;
00837 }
00838 
00839 /**
00840  * Closes the stream and reclaims any resources used by the stream.
00841  * The stream object will be freed, so the client must not access it again.
00842  * @returns NULL in all cases.
00843  */
00844 extern struct ccn_fetch_stream *
00845 ccn_fetch_close(struct ccn_fetch_stream *fs) {
00846         // destroys a ccn_fetch_stream object
00847         // implicit abort of any outstanding fetches
00848         // always returns NULL
00849         int i;
00850     FILE *debug = fs->parent->debug;
00851         ccn_fetch_flags flags = fs->parent->debugFlags;
00852     
00853         // make orphans of all outstanding requests
00854         // CallMe should handle the cleanup
00855         struct localClosure * this = fs->requests;
00856         fs->requests = NULL;
00857         while (this != NULL) {
00858                 this->fs = NULL;
00859                 this = this->next;
00860         }
00861         // free up the buffers
00862         fs->maxBufs = 0;
00863         PruneSegments(fs);
00864         
00865         if (fs->name != NULL)
00866                 ccn_charbuf_destroy(&fs->name);
00867         if (fs->interest != NULL)
00868                 ccn_charbuf_destroy(&fs->interest);
00869         struct ccn_fetch *f = fs->parent;
00870         if (f != NULL) {
00871                 int ns = f->nStreams;
00872                 fs->parent = NULL;
00873                 for (i = 0; i < ns; i++) {
00874                         struct ccn_fetch_stream *tfs = f->streams[i];
00875                         if (tfs == fs) {
00876                                 // found it, so get rid of it
00877                                 ns--;
00878                                 f->nStreams = ns;
00879                                 f->streams[i] = NULL;
00880                                 f->streams[i] = f->streams[ns];
00881                                 f->streams[ns] = NULL;  
00882                                 break;  
00883                         }
00884                 }
00885         }
00886         if (debug != NULL && (flags & ccn_fetch_flags_NoteOpenClose)) {
00887                 fprintf(debug, 
00888                                 "-- ccn_fetch close, %s, segReq %jd, segsRead %jd, timeouts %jd\n",
00889                                 fs->id,
00890                                 fs->segsRequested,
00891                                 fs->segsRead,
00892                                 fs->timeoutsSeen);
00893                 fflush(debug);
00894         }
00895         // finally, get rid of the stream object
00896         freeString(fs->id);
00897         free(fs);
00898         return NULL;
00899 }
00900 
00901 /**
00902  * Tests for available bytes in the stream.
00903  */
00904 extern intmax_t
00905 ccn_fetch_avail(struct ccn_fetch_stream *fs) {
00906         intmax_t pos = fs->readPosition;
00907         if (fs->fileSize >= 0 && pos >= fs->fileSize) {
00908                 // file size known, and we are at the limit
00909                 return CCN_FETCH_READ_END;
00910         }
00911         intmax_t avail = 0;
00912         seg_t seg = fs->readSeg;
00913         if (fs->timeoutSeg >= 0 && seg >= fs->timeoutSeg)
00914                 // timeout indication
00915                 return CCN_FETCH_READ_TIMEOUT;
00916         if (fs->zeroLenSeg >= 0 && seg >= fs->zeroLenSeg)
00917                 // zero len indication
00918                 return CCN_FETCH_READ_ZERO;
00919         seg_t finalSeg = fs->finalSeg;
00920         if (seg > finalSeg && fs->finalSeg >= 0)
00921                 // seek beyond EOF may cause this
00922                 return CCN_FETCH_READ_NONE;
00923         
00924         for (;;) {
00925                 struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, seg);
00926                 if (fb == NULL) break;
00927                 if (fb->pos < 0) fb->pos = pos;
00928                 int len = fb->len;
00929                 if (seg == fs->readSeg) {
00930                         // adjust for offset into the buffer
00931                         intmax_t off = pos - fb->pos;
00932                         if (off > 0) len = len - off;
00933                 }
00934                 avail = avail + len;
00935                 pos = pos + len;
00936                 seg++;
00937         }
00938         if (avail == 0)
00939                 // nothing available at this time, but not at the end, we think
00940                 return CCN_FETCH_READ_NONE;
00941         return avail;
00942 }
00943 
00944 /**
00945  * Reads bytes from a stream.
00946  * Reads at most len bytes into buf from the given stream.
00947  * Will not wait for bytes to arrive.
00948  * Advances the read position on a successful read.
00949  * @returns
00950  *    CCN_FETCH_READ_TIMEOUT if a timeout occurred,
00951  *    CCN_FETCH_READ_ZERO if a zero-length segment was found
00952  *    CCN_FETCH_READ_NONE if no bytes are immediately available
00953  *    CCN_FETCH_READ_END if the stream is at the end,
00954  *    and N > 0 if N bytes were read.
00955  */
00956 extern intmax_t
00957 ccn_fetch_read(struct ccn_fetch_stream *fs,
00958                            void *buf,
00959                            intmax_t len) {
00960         if (len < 0 || buf == NULL) {
00961                 return CCN_FETCH_READ_NONE;
00962         }
00963         intmax_t off = 0;
00964         intmax_t pos = fs->readPosition;
00965         if (fs->fileSize >= 0 && pos >= fs->fileSize) {
00966                 // file size known, and we are at the limit
00967                 return CCN_FETCH_READ_END;
00968         }
00969         intmax_t nr = 0;
00970         unsigned char *dst = (unsigned char *) buf;
00971         seg_t seg = fs->readSeg;
00972         
00973         if (fs->timeoutSeg >= 0 && seg >= fs->timeoutSeg)
00974                 // if a needed read timed out, then we say so
00975                 return CCN_FETCH_READ_TIMEOUT;
00976         if (fs->zeroLenSeg >= 0 && seg >= fs->zeroLenSeg)
00977                 // if we got a zero length segment, report it
00978                 return CCN_FETCH_READ_ZERO;
00979         while (len > 0) {
00980                 struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, seg);
00981                 if (fb == NULL) break;
00982                 unsigned char *src = fb->buf;
00983                 intmax_t start = fb->pos;
00984                 intmax_t lo = start;
00985                 if (lo < 0) {
00986                         // segments delivered at random might cause this
00987                         lo = pos;
00988                         fb->pos = pos;
00989                 }
00990                 intmax_t hi = lo + fb->len;
00991                 if (pos < lo || pos >= hi || seg != fb->seg) {
00992                         // this SHOULD NOT HAPPEN!
00993                         FILE *debug = fs->parent->debug;
00994                         if (debug != NULL) {
00995                                 fprintf(debug, 
00996                                                 "** ccn_fetch read, %s, seg %jd, pos %jd, lo %jd, hi %jd\n",
00997                                                 fs->id, seg, pos, (intmax_t) lo, (intmax_t) hi);
00998                                 fflush(debug);
00999                         }
01000                         break;
01001                 }
01002                 intmax_t d = hi - pos;
01003                 if (d > len) d = len;
01004                 memcpy(dst+off, src+(pos-lo), d);
01005                 nr = nr + d;
01006                 pos = pos + d;
01007                 off = off + d;
01008                 len = len - d;
01009                 fs->readPosition = pos;
01010                 fs->readStart = start;
01011                 if (pos == hi) {
01012                         // finished the bytes in this segment
01013                         seg++;
01014                         fs->readSeg = seg;
01015                         fs->readStart = pos;
01016                 }
01017         }
01018         NeedSegments(fs);
01019         PruneSegments(fs);
01020         if (nr == 0) {
01021                 return CCN_FETCH_READ_NONE;
01022         }
01023         return nr;
01024 }
01025 
01026 /**
01027  * Resets the timeout marker.
01028  */
01029 extern void
01030 ccn_reset_timeout(struct ccn_fetch_stream *fs) {
01031         fs->timeoutSeg = -1;
01032         fs->segsAhead = 0;
01033 }
01034 
01035 /**
01036  * Seeks to a position in a stream.
01037  * Sets the read position.
01038  * It is strongly recommended that the seek is only done to a position that
01039  * is either 0 or has resulted from a successful read.  Otherwise
01040  * end of stream indicators may be returned for a seek beyond the end.
01041  * @returns -1 if the seek is to a bad position, otherwise returns 0.
01042  */
01043 extern int
01044 ccn_fetch_seek(struct ccn_fetch_stream *fs, intmax_t pos) {
01045         // seeks to the given position in the input stream
01046         seg_t seg = 0;
01047         intmax_t start = 0;
01048         if (pos == 0) {
01049                 // seek to the start should always be OK
01050                 // (also resets bad segment indicators)
01051                 fs->timeoutSeg = -1;
01052                 fs->zeroLenSeg = -1;
01053                 fs->segsAhead = 0;
01054         } else if (pos == fs->readPosition) {
01055                 // no change
01056                 return 0;
01057         } else {
01058                 // seek elsewhere
01059                 struct ccn_fetch_buffer *fb = FindBufferForPosition(fs, pos);
01060                 if (fb != NULL) {
01061                         // an existing segment, so this is easy
01062                         seg = fb->seg;
01063                         start = fb->pos;
01064                 } else {
01065                         int ss = fs->segSize;
01066                         if (pos < 0 || ss <= 0)
01067                                 // segment size is not known, so indicate that seek fails
01068                                 return -1;
01069                         intmax_t fileSize = fs->fileSize;
01070                         if (fileSize >= 0 && pos > fileSize) {
01071                                 // file size is known exactly, and we have gone too far
01072                                 return -1;
01073                         }
01074                         // at this point we can set the position (failure can occur later on the read)
01075                         seg = pos / ss;
01076                         start = seg * ss;
01077                 }
01078         }
01079         fs->readPosition = pos;
01080         fs->readStart = start;
01081         fs->readSeg = seg;
01082         NeedSegment(fs, seg);
01083         PruneSegments(fs);
01084         
01085         return 0;
01086 }
01087 
01088 /**
01089  * @returns the current read position.
01090  */
01091 extern intmax_t
01092 ccn_fetch_position(struct ccn_fetch_stream *fs) {
01093         return fs->readPosition;
01094 }
01095 
01096 

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