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:
00531                         if (fs->timeoutSeg >= 0 && fs->timeoutSeg <= thisSeg)
00532                                 // we will ignore this, since we are blocked
00533                                 return(CCN_UPCALL_RESULT_OK);
00534                         break;
00535                 default:
00536                         // SHOULD NOT HAPPEN
00537                         return(CCN_UPCALL_RESULT_ERR);
00538     }
00539         
00540         struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, thisSeg);
00541         if (fb == NULL) {
00542                 // we don't already have the data yet
00543                 const unsigned char *data = NULL;
00544                 size_t dataLen = 0;
00545                 size_t ccnb_size = info->pco->offset[CCN_PCO_E];
00546                 const unsigned char *ccnb = info->content_ccnb;
00547                 int res = ccn_content_get_value(ccnb, ccnb_size, info->pco,
00548                                                                                 &data, &dataLen);
00549                 
00550                 if (res < 0 || (thisSeg != finalSeg && dataLen == 0)) {
00551                         // we got a bogus result, no data in this content!
00552                         if (debug != NULL && (flags & ccn_fetch_flags_NoteAddRem)) {
00553                                 fprintf(debug, 
00554                                                 "-- ccn_fetch no data, %s, seg %jd, final %jd",
00555                                                 fs->id, thisSeg, finalSeg);
00556                                 ShowDelta(debug, req->startClock);
00557                         }
00558                         if (fs->zeroLenSeg < 0 || thisSeg < fs->zeroLenSeg)
00559                                 // note this problem for future reporting
00560                                 fs->zeroLenSeg = thisSeg;
00561                 } else if (thisSeg == finalSeg && dataLen == 0) {
00562                         // EOF, but no buffer needed
00563                         if (fs->fileSize < 0)
00564                                 fs->fileSize = InferPosition(fs, thisSeg);
00565                         fs->finalSeg = finalSeg-1;
00566                         if (debug != NULL && (flags & ccn_fetch_flags_NoteFinal)) {
00567                                 fprintf(debug, 
00568                                                 "-- ccn_fetch EOF, %s, seg %jd, len %d, fs %jd",
00569                                                 fs->id, thisSeg,
00570                                                 (int) dataLen,
00571                                                 fs->fileSize);
00572                                 ShowDelta(debug, req->startClock);
00573                         }
00574                         
00575                 } else {
00576                         // alloc a buffer and transfer the data
00577                         
00578                         if (fs->segSize == 0) {
00579                                 // assuming fixed size segments, so any should do
00580                                 // EXCEPT for an incomplete final segment
00581                                 if (thisSeg == 0 || thisSeg < finalSeg)
00582                                         fs->segSize = dataLen;
00583                         }
00584                         if (thisSeg == finalSeg) fs->finalSegLen = dataLen;
00585                         struct ccn_fetch_buffer *fb = NewBufferForSeg(fs, thisSeg, dataLen);
00586                         memcpy(fb->buf, data, dataLen);
00587                         if (debug != NULL && (flags & ccn_fetch_flags_NoteFill)) {
00588                                 fprintf(debug, 
00589                                                 "-- ccn_fetch FillSeg, %s, seg %jd, len %d, nbuf %d",
00590                                                 fs->id, thisSeg, (int) dataLen, (int) fs->nBufs);
00591                                 ShowDelta(debug, req->startClock);
00592                         }
00593                         if (thisSeg == finalSeg) {
00594                                 // the file size is known in segments
00595                                 if (fs->segSize <= 0) {
00596                                         // variable or unknown segment size
00597                                         if (fb->pos >= 0) {
00598                                                 fs->fileSize = fb->pos + dataLen;
00599                                         }
00600                                 } else {
00601                                         // fixed segment size, so file size is now known
00602                                         fs->fileSize = thisSeg * fs->segSize + dataLen;
00603                                 }
00604                                 if (debug != NULL && (flags & ccn_fetch_flags_NoteFinal)) {
00605                                         fprintf(debug, 
00606                                                         "-- ccn_fetch EOF, %s, seg %jd, len %d, fs %jd",
00607                                                         fs->id, thisSeg, (int) dataLen, fs->fileSize);
00608                                         ShowDelta(debug, req->startClock);
00609                                 }
00610                         }
00611                         fs->segsRead++;
00612                 }
00613         }
00614         
00615         ccn_set_run_timeout(fs->parent->h, 0);
00616         return(CCN_UPCALL_RESULT_OK);
00617 }
00618 
00619 ///////////////////////////////////////////////////////
00620 // External routines
00621 ///////////////////////////////////////////////////////
00622 
00623 /**
00624  * Creates a new ccn_fetch object using the given ccn connection.
00625  * If h == NULL, attempts to create a new connection automatically.
00626  * @returns NULL if the creation was not successful
00627  * (only can happen for the h == NULL case).
00628  */
00629 extern struct ccn_fetch *
00630 ccn_fetch_new(struct ccn *h) {
00631         struct ccn_fetch *f = calloc(1, sizeof(*f));
00632         if (h == NULL) {
00633                 h = ccn_create();
00634                 int connRes = ccn_connect(h, NULL);
00635                 if (connRes < 0) {
00636                         ccn_destroy(&h);
00637                         free(f);
00638                         return NULL;
00639                 }
00640                 f->localConnect = 1;
00641         }
00642         f->h = h;
00643         return f;
00644 }
00645 
00646 void
00647 ccn_fetch_set_debug(struct ccn_fetch *f, FILE *debug, ccn_fetch_flags flags) {
00648         f->debug = debug;
00649         f->debugFlags = flags;
00650 }
00651 
00652 /**
00653  * Destroys a ccn_fetch object.
00654  * Only destroys the underlying ccn connection if it was automatically created.
00655  * Forces all underlying streams to close immediately.
00656  * @returns NULL in all cases.
00657  */
00658 extern struct ccn_fetch *
00659 ccn_fetch_destroy(struct ccn_fetch *f) {
00660         // destroys a ccn_fetch object
00661         // always returns NULL
00662         // only destroys the underlying ccn connection if it was
00663         // automatically created, otherwise does not alter it
00664         if (f != NULL) {
00665                 struct ccn *h = f->h;
00666                 if (h != NULL && f->localConnect) {
00667                         ccn_disconnect(h);
00668                         ccn_destroy(&f->h);
00669                 }
00670                 // take down all of the streams
00671                 while (f->nStreams > 0) {
00672                         struct ccn_fetch_stream *fs = f->streams[0];
00673                         if (fs == NULL) break;
00674                         ccn_fetch_close(fs);
00675                 }
00676                 free(f);
00677         }
00678         return NULL;
00679 }
00680 
00681 /**
00682  * Polls the underlying streams and attempts to make progress.
00683  * Scans the streams for those that have data already present, or are at the end
00684  * of the stream.  If the count is 0, perfoms a ccn_poll on the underlying
00685  * ccn connection with a 0 timeout.
00686  *
00687  * NOTE: periodic calls to ccn_fetch_poll should be performed to update
00688  * the contents of the streams UNLESS the client is calling ccn_run for
00689  * the underlying ccn connection.
00690  * @returns the count of streams that have pending data or have ended.
00691  */
00692 extern int
00693 ccn_fetch_poll(struct ccn_fetch *f) {
00694         int i;
00695     int count = 0;
00696         int ns = f->nStreams;
00697         for (i = 0; i < ns; i++) {
00698                 struct ccn_fetch_stream *fs = f->streams[i];
00699                 if (fs != NULL) {
00700                         intmax_t avail = ccn_fetch_avail(fs);
00701                         if (avail >= 0) count++;
00702                 }
00703         }
00704         // we should try for more progress
00705         ccn_run(f->h, 0);
00706         return count;
00707 }
00708 
00709 /**
00710  * Provides an iterator through the underlying streams.
00711  * Use fs == NULL to start the iteration, and an existing stream to continue
00712  * the iteration.
00713  * @returns the next stream in the iteration, or NULL at the end.
00714  * Note that providing a stale (closed) stream handle will return NULL.
00715  */
00716 extern struct ccn_fetch_stream *
00717 ccn_fetch_next(struct ccn_fetch *f, struct ccn_fetch_stream *fs) {
00718         int i;
00719     int ns = f->nStreams;
00720     struct ccn_fetch_stream *lag = NULL;
00721         for (i = 0; i < ns; i++) {
00722                 struct ccn_fetch_stream *tfs = f->streams[i];
00723                 if (tfs != NULL) {
00724                         if (lag == fs) return tfs;
00725                         lag = tfs;
00726                 }
00727         }
00728         return NULL;
00729 }
00730 
00731 /**
00732  * @returns the underlying ccn connection.
00733  */
00734 extern struct ccn *
00735 ccn_fetch_get_ccn(struct ccn_fetch *f) {
00736         return f->h;
00737 }
00738 
00739 /**
00740  * Creates a stream for a named interest.
00741  * The name should be a ccnb encoded interest.
00742  * If resolveVersion, then we assume that the version is unresolved, 
00743  * and an attempt is made to determine the version number using the highest
00744  * version.
00745  * The number of buffers (nBufs) may be silently limited.
00746  * @returns NULL if the stream creation failed,
00747  * otherwise returns the new stream.
00748  */
00749 extern struct ccn_fetch_stream *
00750 ccn_fetch_open(struct ccn_fetch *f,
00751                            struct ccn_charbuf *name,
00752                            const char *id,
00753                            struct ccn_charbuf *interestTemplate,
00754                            int maxBufs,
00755                            int resolveVersion,
00756                            int assumeFixed) {
00757         // returns a new ccn_fetch_stream object based on the arguments
00758         // returns NULL if not successful
00759     if (maxBufs <= 0) return NULL;
00760         if (maxBufs > 16) maxBufs = 16;
00761         int res = 0;
00762         FILE *debug = f->debug;
00763         ccn_fetch_flags flags = f->debugFlags;
00764     
00765         // first, resolve the version
00766         struct ccn_fetch_stream *fs = calloc(1, sizeof(*fs));
00767         fs->segSize = (assumeFixed ? 0 : -1);
00768         fs->name = ccn_charbuf_create();
00769         fs->id = newStringCopy(id);
00770         ccn_charbuf_append_charbuf(fs->name, name);
00771         if (resolveVersion) {
00772                 int tmInc = 40; // TBD: need better strategy for version timeout
00773                 int tm = 0;
00774                 while (tm < CCN_VERSION_TIMEOUT) {
00775                         res = ccn_resolve_version(f->h, fs->name, resolveVersion, tmInc);
00776                         if (res >= 0) break;
00777                         tm = tm + tmInc;
00778                 }
00779                 if (res < 0) {
00780                         // could not resolve version for this name
00781                         // get rid of allocations so far and bail out
00782                         if (debug != NULL && (flags & ccn_fetch_flags_NoteOpenClose)) {
00783                                 fprintf(debug, 
00784                                                 "-- ccn_fetch open, %s, failed to resolve version\n",
00785                                                 fs->id);
00786                                 fflush(debug);
00787                         }
00788                         ccn_charbuf_destroy(&fs->name);
00789                         freeString(fs->id);
00790                         free(fs);
00791                         return NULL;
00792                 }
00793         }
00794         fs->maxBufs = maxBufs;
00795         fs->segsAhead = 0;
00796         fs->fileSize = -1;
00797         fs->finalSeg = -1;
00798         fs->timeoutSeg = -1;
00799         fs->zeroLenSeg = -1;
00800         fs->parent = f;
00801         fs->timeoutUSecs = CCN_INTEREST_TIMEOUT_USECS;  // TBD: how to get better timeout?
00802         
00803         // use the supplied template or the default
00804         if (interestTemplate != NULL) {
00805                 struct ccn_charbuf *cb = ccn_charbuf_create();
00806                 ccn_charbuf_append_charbuf(cb, interestTemplate);
00807                 fs->interest = cb;
00808         } else
00809                 fs->interest = make_data_template(MaxSuffixDefault);
00810         
00811         
00812         // remember the stream in the parent
00813         int ns = f->nStreams;
00814         int max = f->maxStreams;
00815         if (ns >= max) {
00816                 // extend the vector
00817                 int nMax = max+max/2+4;
00818         f->streams = realloc(f->streams, sizeof(*(f->streams)) * nMax);
00819                 f->maxStreams = nMax;
00820         }
00821         // guaranteed room to add at the end
00822         f->streams[ns] = fs;
00823         f->nStreams = ns+1;
00824         
00825         if (debug != NULL && (flags & ccn_fetch_flags_NoteOpenClose)) {
00826                 fprintf(debug, 
00827                                 "-- ccn_fetch open, %s\n",
00828                                 fs->id);
00829                 fflush(debug);
00830         }
00831         // prep for the first segment
00832         NeedSegment(fs, 0);
00833         return fs;
00834 }
00835 
00836 /**
00837  * Closes the stream and reclaims any resources used by the stream.
00838  * The stream object will be freed, so the client must not access it again.
00839  * @returns NULL in all cases.
00840  */
00841 extern struct ccn_fetch_stream *
00842 ccn_fetch_close(struct ccn_fetch_stream *fs) {
00843         // destroys a ccn_fetch_stream object
00844         // implicit abort of any outstanding fetches
00845         // always returns NULL
00846         int i;
00847     FILE *debug = fs->parent->debug;
00848         ccn_fetch_flags flags = fs->parent->debugFlags;
00849     
00850         // make orphans of all outstanding requests
00851         // CallMe should handle the cleanup
00852         struct localClosure * this = fs->requests;
00853         fs->requests = NULL;
00854         while (this != NULL) {
00855                 this->fs = NULL;
00856                 this = this->next;
00857         }
00858         // free up the buffers
00859         fs->maxBufs = 0;
00860         PruneSegments(fs);
00861         
00862         if (fs->name != NULL)
00863                 ccn_charbuf_destroy(&fs->name);
00864         if (fs->interest != NULL)
00865                 ccn_charbuf_destroy(&fs->interest);
00866         struct ccn_fetch *f = fs->parent;
00867         if (f != NULL) {
00868                 int ns = f->nStreams;
00869                 fs->parent = NULL;
00870                 for (i = 0; i < ns; i++) {
00871                         struct ccn_fetch_stream *tfs = f->streams[i];
00872                         if (tfs == fs) {
00873                                 // found it, so get rid of it
00874                                 ns--;
00875                                 f->nStreams = ns;
00876                                 f->streams[i] = NULL;
00877                                 f->streams[i] = f->streams[ns];
00878                                 f->streams[ns] = NULL;  
00879                                 break;  
00880                         }
00881                 }
00882         }
00883         if (debug != NULL && (flags & ccn_fetch_flags_NoteOpenClose)) {
00884                 fprintf(debug, 
00885                                 "-- ccn_fetch close, %s, segReq %jd, segsRead %jd, timeouts %jd\n",
00886                                 fs->id,
00887                                 fs->segsRequested,
00888                                 fs->segsRead,
00889                                 fs->timeoutsSeen);
00890                 fflush(debug);
00891         }
00892         // finally, get rid of the stream object
00893         freeString(fs->id);
00894         free(fs);
00895         return NULL;
00896 }
00897 
00898 /**
00899  * Tests for available bytes in the stream.
00900  */
00901 extern intmax_t
00902 ccn_fetch_avail(struct ccn_fetch_stream *fs) {
00903         intmax_t pos = fs->readPosition;
00904         if (fs->fileSize >= 0 && pos >= fs->fileSize) {
00905                 // file size known, and we are at the limit
00906                 return CCN_FETCH_READ_END;
00907         }
00908         intmax_t avail = 0;
00909         seg_t seg = fs->readSeg;
00910         if (fs->timeoutSeg >= 0 && seg >= fs->timeoutSeg)
00911                 // timeout indication
00912                 return CCN_FETCH_READ_TIMEOUT;
00913         if (fs->zeroLenSeg >= 0 && seg >= fs->zeroLenSeg)
00914                 // zero len indication
00915                 return CCN_FETCH_READ_ZERO;
00916         seg_t finalSeg = fs->finalSeg;
00917         if (seg > finalSeg && fs->finalSeg >= 0)
00918                 // seek beyond EOF may cause this
00919                 return CCN_FETCH_READ_NONE;
00920         
00921         for (;;) {
00922                 struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, seg);
00923                 if (fb == NULL) break;
00924                 if (fb->pos < 0) fb->pos = pos;
00925                 int len = fb->len;
00926                 if (seg == fs->readSeg) {
00927                         // adjust for offset into the buffer
00928                         intmax_t off = pos - fb->pos;
00929                         if (off > 0) len = len - off;
00930                 }
00931                 avail = avail + len;
00932                 pos = pos + len;
00933                 seg++;
00934         }
00935         if (avail == 0)
00936                 // nothing available at this time, but not at the end, we think
00937                 return CCN_FETCH_READ_NONE;
00938         return avail;
00939 }
00940 
00941 /**
00942  * Reads bytes from a stream.
00943  * Reads at most len bytes into buf from the given stream.
00944  * Will not wait for bytes to arrive.
00945  * Advances the read position on a successful read.
00946  * @returns
00947  *    CCN_FETCH_READ_TIMEOUT if a timeout occured,
00948  *    CCN_FETCH_READ_ZERO if a zero-length segment was found
00949  *    CCN_FETCH_READ_NONE if no bytes are immediately available
00950  *    CCN_FETCH_READ_END if the stream is at the end,
00951  *    and N > 0 if N bytes were read.
00952  */
00953 extern intmax_t
00954 ccn_fetch_read(struct ccn_fetch_stream *fs,
00955                            void *buf,
00956                            intmax_t len) {
00957         if (len < 0 || buf == NULL) {
00958                 return CCN_FETCH_READ_NONE;
00959         }
00960         intmax_t off = 0;
00961         intmax_t pos = fs->readPosition;
00962         if (fs->fileSize >= 0 && pos >= fs->fileSize) {
00963                 // file size known, and we are at the limit
00964                 return CCN_FETCH_READ_END;
00965         }
00966         intmax_t nr = 0;
00967         unsigned char *dst = (unsigned char *) buf;
00968         seg_t seg = fs->readSeg;
00969         
00970         if (fs->timeoutSeg >= 0 && seg >= fs->timeoutSeg)
00971                 // if a needed read timed out, then we say so
00972                 return CCN_FETCH_READ_TIMEOUT;
00973         if (fs->zeroLenSeg >= 0 && seg >= fs->zeroLenSeg)
00974                 // if we got a zero length segment, report it
00975                 return CCN_FETCH_READ_ZERO;
00976         while (len > 0) {
00977                 struct ccn_fetch_buffer *fb = FindBufferForSeg(fs, seg);
00978                 if (fb == NULL) break;
00979                 unsigned char *src = fb->buf;
00980                 intmax_t start = fb->pos;
00981                 intmax_t lo = start;
00982                 if (lo < 0) {
00983                         // segments delivered at random might cause this
00984                         lo = pos;
00985                         fb->pos = pos;
00986                 }
00987                 intmax_t hi = lo + fb->len;
00988                 if (pos < lo || pos >= hi || seg != fb->seg) {
00989                         // this SHOULD NOT HAPPEN!
00990                         FILE *debug = fs->parent->debug;
00991                         if (debug != NULL) {
00992                                 fprintf(debug, 
00993                                                 "** ccn_fetch read, %s, seg %jd, pos %jd, lo %jd, hi %jd\n",
00994                                                 fs->id, seg, pos, (intmax_t) lo, (intmax_t) hi);
00995                                 fflush(debug);
00996                         }
00997                         break;
00998                 }
00999                 intmax_t d = hi - pos;
01000                 if (d > len) d = len;
01001                 memcpy(dst+off, src+(pos-lo), d);
01002                 nr = nr + d;
01003                 pos = pos + d;
01004                 off = off + d;
01005                 len = len - d;
01006                 fs->readPosition = pos;
01007                 fs->readStart = start;
01008                 if (pos == hi) {
01009                         // finished the bytes in this segment
01010                         seg++;
01011                         fs->readSeg = seg;
01012                         fs->readStart = pos;
01013                 }
01014         }
01015         NeedSegments(fs);
01016         PruneSegments(fs);
01017         if (nr == 0) {
01018                 return CCN_FETCH_READ_NONE;
01019         }
01020         return nr;
01021 }
01022 
01023 /**
01024  * Resets the timeout marker.
01025  */
01026 extern void
01027 ccn_reset_timeout(struct ccn_fetch_stream *fs) {
01028         fs->timeoutSeg = -1;
01029         fs->segsAhead = 0;
01030 }
01031 
01032 /**
01033  * Seeks to a position in a stream.
01034  * Sets the read position.
01035  * It is strongly recommended that the seek is only done to a position that
01036  * is either 0 or has resulted from a successful read.  Otherwise
01037  * end of stream indicators may be returned for a seek beyond the end.
01038  * @returns -1 if the seek is to a bad position, otherwise returns 0.
01039  */
01040 extern int
01041 ccn_fetch_seek(struct ccn_fetch_stream *fs, intmax_t pos) {
01042         // seeks to the given position in the input stream
01043         seg_t seg = 0;
01044         intmax_t start = 0;
01045         if (pos == 0) {
01046                 // seek to the start should always be OK
01047                 // (also resets bad segment indicators)
01048                 fs->timeoutSeg = -1;
01049                 fs->zeroLenSeg = -1;
01050                 fs->segsAhead = 0;
01051         } else if (pos == fs->readPosition) {
01052                 // no change
01053                 return 0;
01054         } else {
01055                 // seek elsewhere
01056                 struct ccn_fetch_buffer *fb = FindBufferForPosition(fs, pos);
01057                 if (fb != NULL) {
01058                         // an existing segment, so this is easy
01059                         seg = fb->seg;
01060                         start = fb->pos;
01061                 } else {
01062                         int ss = fs->segSize;
01063                         if (pos < 0 || ss <= 0)
01064                                 // segment size is not known, so indicate that seek fails
01065                                 return -1;
01066                         intmax_t fileSize = fs->fileSize;
01067                         if (fileSize >= 0 && pos > fileSize) {
01068                                 // file size is known exactly, and we have gone too far
01069                                 return -1;
01070                         }
01071                         // at this point we can set the position (failure can occur later on the read)
01072                         seg = pos / ss;
01073                         start = seg * ss;
01074                 }
01075         }
01076         fs->readPosition = pos;
01077         fs->readStart = start;
01078         fs->readSeg = seg;
01079         NeedSegment(fs, seg);
01080         PruneSegments(fs);
01081         
01082         return 0;
01083 }
01084 
01085 /**
01086  * @returns the current read position.
01087  */
01088 extern intmax_t
01089 ccn_fetch_position(struct ccn_fetch_stream *fs) {
01090         return fs->readPosition;
01091 }
01092 
01093 
Generated on Fri May 13 16:27:02 2011 for Content-Centric Networking in C by  doxygen 1.6.3