*/
#define BLOOMFILTER_K 16
+/**
+ * Number of availability trials we perform per search result.
+ */
+#define AVAILABILITY_TRIALS_MAX 8
+
/**
* By how much (in ms) do we decrement the TTL
* at each hop?
GNUNET_FS_dequeue_ (struct GNUNET_FS_QueueEntry *qh);
+
+/**
+ * Notification of FS that a search probe has made progress.
+ * This function is used INSTEAD of the client's event handler
+ * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
+ *
+ * @param cls closure, always NULL (!), actual closure
+ * is in the client-context of the info struct
+ * @param info details about the event, specifying the event type
+ * and various bits about the event
+ * @return client-context (for the next progress call
+ * for this operation; should be set to NULL for
+ * SUSPEND and STOPPED events). The value returned
+ * will be passed to future callbacks in the respective
+ * field in the GNUNET_FS_ProgressInfo struct.
+ */
+void*
+GNUNET_FS_search_probe_progress_ (void *cls,
+ const struct GNUNET_FS_ProgressInfo *info);
+
+
+
/**
* Master context for most FS operations.
*/
char *filename;
/**
- * Connection to the FS service,
- * only valid during the UNINDEX_STATE_FS_NOTIFY
- * phase.
+ * Connection to the FS service, only valid during the
+ * UNINDEX_STATE_FS_NOTIFY phase.
*/
struct GNUNET_CLIENT_Connection *client;
/**
- * Connection to the datastore service,
- * only valid during the UNINDEX_STATE_DS_NOTIFY
- * phase.
+ * Connection to the datastore service, only valid during the
+ * UNINDEX_STATE_DS_NOTIFY phase.
*/
struct GNUNET_DATASTORE_Handle *dsh;
struct GNUNET_TIME_Absolute start_time;
/**
- * Hash of the file's contents (once
- * computed).
+ * Hash of the file's contents (once computed).
*/
GNUNET_HashCode file_id;
{
/**
- * URI to which this search result
- * refers to.
+ * Search context this result belongs to.
+ */
+ struct GNUNET_FS_SearchContext *sc;
+
+ /**
+ * URI to which this search result refers to.
*/
struct GNUNET_FS_Uri *uri;
void *client_info;
/**
- * ID of a job that is currently probing
- * this results' availability (NULL if we
- * are not currently probing).
+ * ID of a job that is currently probing this results' availability
+ * (NULL if we are not currently probing).
*/
struct GNUNET_FS_DownloadContext *probe_ctx;
-
+
/**
- * ID of the task that will clean up the probe_ctx
- * should it not complete on time (and that will
- * need to be cancelled if we clean up the search
- * result before then).
+ * ID of the task that will clean up the probe_ctx should it not
+ * complete on time (and that will need to be cancelled if we clean
+ * up the search result before then).
*/
GNUNET_SCHEDULER_TaskIdentifier probe_cancel_task;
/**
- * Number of mandatory keywords for which
- * we have NOT yet found the search result;
- * when this value hits zero, the search
- * result is given to the callback.
+ * When did the current probe become active?
+ */
+ struct GNUNET_TIME_Absolute probe_active_time;
+
+ /**
+ * How much longer should we run the current probe before giving up?
+ */
+ struct GNUNET_TIME_Relative remaining_probe_time;
+
+ /**
+ * Number of mandatory keywords for which we have NOT yet found the
+ * search result; when this value hits zero, the search result is
+ * given to the callback.
*/
uint32_t mandatory_missing;
/**
- * Number of optional keywords under which
- * this result was also found.
+ * Number of optional keywords under which this result was also
+ * found.
*/
uint32_t optional_support;
/**
- * Number of availability tests that
- * have succeeded for this result.
+ * Number of availability tests that have succeeded for this result.
*/
uint32_t availability_success;
/**
- * Number of availability trials that we
- * have performed for this search result.
+ * Number of availability trials that we have performed for this
+ * search result.
*/
uint32_t availability_trials;
struct GNUNET_FS_Uri *uri;
/**
- * For update-searches, link to the
- * base-SKS search that triggered the
- * update search; otherwise NULL.
+ * For update-searches, link to the base-SKS search that triggered
+ * the update search; otherwise NULL.
*/
struct GNUNET_FS_SearchContext *parent;
/**
- * For update-searches, link to the
- * first child search that triggered the
- * update search; otherwise NULL.
+ * For update-searches, link to the first child search that
+ * triggered the update search; otherwise NULL.
*/
struct GNUNET_FS_SearchContext *child_head;
/**
- * For update-searches, link to the
- * last child search that triggered the
- * update search; otherwise NULL.
+ * For update-searches, link to the last child search that triggered
+ * the update search; otherwise NULL.
*/
struct GNUNET_FS_SearchContext *child_tail;
/**
- * For update-searches, link to the
- * next child belonging to the same parent.
+ * For update-searches, link to the next child belonging to the same
+ * parent.
*/
struct GNUNET_FS_SearchContext *next;
/**
- * For update-searches, link to the
- * previous child belonging to the same
- * parent.
+ * For update-searches, link to the previous child belonging to the
+ * same parent.
*/
struct GNUNET_FS_SearchContext *prev;
struct GNUNET_CONTAINER_MultiHashMap *master_result_map;
/**
- * Per-keyword information for a keyword search.
- * This array will have exactly as many entries
- * as there were keywords.
+ * Per-keyword information for a keyword search. This array will
+ * have exactly as many entries as there were keywords.
*/
struct SearchRequestEntry *requests;
struct GNUNET_TIME_Absolute start_time;
/**
- * ID of a task that is using this struct
- * and that must be cancelled when the search
- * is being stopped (if not GNUNET_SCHEDULER_NO_TASK).
- * Used for the task that adds some artificial
- * delay when trying to reconnect to the FS
- * service.
+ * ID of a task that is using this struct and that must be cancelled
+ * when the search is being stopped (if not
+ * GNUNET_SCHEDULER_NO_TASK). Used for the task that adds some
+ * artificial delay when trying to reconnect to the FS service.
*/
GNUNET_SCHEDULER_TaskIdentifier task;
* Number of mandatory keywords in this query.
*/
uint32_t mandatory_count;
+
+
};
struct DownloadRequest
{
/**
- * While pending, we keep all download requests
- * in a linked list.
+ * While pending, we keep all download requests in a linked list.
*/
struct DownloadRequest *next;
* @author Christian Grothoff
*
* TODO:
- * - location URI suppport (can wait, easy)
* - persistence (can wait)
+ * - location URI suppport (can wait, easy)
+ * - different priority for scheduling probe downloads?
* - check if iblocks can be computed from existing blocks (can wait, hard)
*/
#include "platform.h"
/**
- * Fill in all of the generic fields for
- * a download event.
+ * Fill in all of the generic fields for a download event and call the
+ * callback.
*
* @param pi structure to fill in
* @param dc overall download context
= GNUNET_TIME_calculate_eta (dc->start_time,
dc->completed,
dc->length);
+ if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
+ dc->client_info = dc->h->upcb (dc->h->upcb_cls,
+ pi);
+ else
+ dc->client_info = GNUNET_FS_search_probe_progress_ (NULL,
+ pi);
}
/**
/* signal completion */
pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
make_download_status (&pi, dc);
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
if (dc->parent != NULL)
check_completed (dc->parent);
}
dc->emsg = GNUNET_strdup ("Internal error or bogus download URI");
/* signal error */
pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
- make_download_status (&pi, dc);
pi.value.download.specifics.error.message = dc->emsg;
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
+ make_download_status (&pi, dc);
/* abort all pending requests */
if (NULL != dc->th)
{
/* signal error */
pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
- make_download_status (&pi, dc);
pi.value.download.specifics.error.message = emsg;
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
-
+ make_download_status (&pi, dc);
/* abort all pending requests */
if (NULL != dc->th)
{
}
pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
- make_download_status (&pi, dc);
pi.value.download.specifics.progress.data = pt;
pi.value.download.specifics.progress.offset = sm->offset;
pi.value.download.specifics.progress.data_len = prc->size;
pi.value.download.specifics.progress.depth = sm->depth;
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
+ make_download_status (&pi, dc);
GNUNET_assert (dc->completed <= dc->length);
if (dc->completed == dc->length)
{
/* signal completion */
pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
make_download_status (&pi, dc);
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
if (dc->parent != NULL)
check_completed (dc->parent);
}
GNUNET_TIME_UNIT_FOREVER_REL);
pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
make_download_status (&pi, dc);
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
GNUNET_CONTAINER_multihashmap_iterate (dc->active,
&retry_entry,
dc);
}
pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
make_download_status (&pi, dc);
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
}
#endif
// FIXME: make persistent
pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
- make_download_status (&pi, dc);
pi.value.download.specifics.start.meta = meta;
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
+ make_download_status (&pi, dc);
schedule_block_download (dc,
&dc->uri->data.chk.chk,
0,
pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
make_download_status (&pi, dc);
- dc->client_info = dc->h->upcb (dc->h->upcb_cls,
- &pi);
-
if (GNUNET_SCHEDULER_NO_TASK != dc->task)
GNUNET_SCHEDULER_cancel (dc->h->sched,
dc->task);
*
* @param pi structure to fill in
* @param sc overall search context
+ * @return value returned by the callback
*/
-static void
+static void *
make_search_status (struct GNUNET_FS_ProgressInfo *pi,
struct GNUNET_FS_SearchContext *sc)
{
= sc->uri;
pi->value.search.duration = GNUNET_TIME_absolute_get_duration (sc->start_time);
pi->value.search.anonymity = sc->anonymity;
+ return sc->h->upcb (sc->h->upcb_cls,
+ pi);
}
struct GNUNET_FS_ProgressInfo pi;
pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
- make_search_status (&pi, sc);
pi.value.search.specifics.result.meta = sr->meta;
pi.value.search.specifics.result.uri = sr->uri;
- sr->client_info = sc->h->upcb (sc->h->upcb_cls,
- &pi);
+ sr->client_info = make_search_status (&pi, sc);
}
struct GNUNET_FS_ProgressInfo pi;
pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
- make_search_status (&pi, sc);
pi.value.search.specifics.update.cctx = sr->client_info;
pi.value.search.specifics.update.meta = sr->meta;
pi.value.search.specifics.update.uri = sr->uri;
= sr->availability_trials;
pi.value.search.specifics.update.applicability_rank
= sr->optional_support;
- sr->client_info = sc->h->upcb (sc->h->upcb_cls,
- &pi);
+ sr->client_info = make_search_status (&pi, sc);
}
}
+/**
+ * Start download probes for the given search result.
+ *
+ * @param sr the search result
+ */
+static void
+start_probe (struct SearchResult *sr);
+
+
+/**
+ * Signal result of last probe to client and then schedule next
+ * probe.
+ */
+static void
+signal_probe_result (struct SearchResult *sr)
+{
+ struct GNUNET_FS_ProgressInfo pi;
+
+ pi.status = GNUNET_FS_STATUS_SEARCH_START;
+ pi.value.search.specifics.update.cctx = sr->client_info;
+ pi.value.search.specifics.update.meta = sr->meta;
+ pi.value.search.specifics.update.uri = sr->uri;
+ pi.value.search.specifics.update.availability_rank = sr->availability_success;
+ pi.value.search.specifics.update.availability_certainty = sr->availability_trials;
+ pi.value.search.specifics.update.applicability_rank = sr->optional_support;
+ sr->sc->client_info = make_search_status (&pi, sr->sc);
+ start_probe (sr);
+}
+
+
+/**
+ * Handle the case where we have failed to receive a response for our probe.
+ *
+ * @param cls our 'struct SearchResult*'
+ * @param tc scheduler context
+ */
+static void
+probe_failure_handler (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct SearchResult *sr = cls;
+ sr->availability_trials++;
+ signal_probe_result (sr);
+}
+
+
+/**
+ * Handle the case where we have gotten a response for our probe.
+ *
+ * @param cls our 'struct SearchResult*'
+ * @param tc scheduler context
+ */
+static void
+probe_success_handler (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct SearchResult *sr = cls;
+ sr->availability_trials++;
+ sr->availability_success++;
+ signal_probe_result (sr);
+}
+
+
+/**
+ * Notification of FS that a search probe has made progress.
+ * This function is used INSTEAD of the client's event handler
+ * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
+ *
+ * @param cls closure, always NULL (!), actual closure
+ * is in the client-context of the info struct
+ * @param info details about the event, specifying the event type
+ * and various bits about the event
+ * @return client-context (for the next progress call
+ * for this operation; should be set to NULL for
+ * SUSPEND and STOPPED events). The value returned
+ * will be passed to future callbacks in the respective
+ * field in the GNUNET_FS_ProgressInfo struct.
+ */
+void*
+GNUNET_FS_search_probe_progress_ (void *cls,
+ const struct GNUNET_FS_ProgressInfo *info)
+{
+ struct SearchResult *sr = info->value.download.cctx;
+ struct GNUNET_TIME_Relative dur;
+
+ switch (info->status)
+ {
+ case GNUNET_FS_STATUS_DOWNLOAD_START:
+ /* ignore */
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
+ /* probes should never be resumed */
+ GNUNET_assert (0);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
+ /* probes should never be suspended */
+ GNUNET_break (0);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
+ /* ignore */
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+ sr->remaining_probe_time,
+ &probe_failure_handler,
+ sr);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+ sr->remaining_probe_time,
+ &probe_success_handler,
+ sr);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
+ /* FIXME: clean up? schedule next probe? or already done? */
+ sr = NULL;
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
+ GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
+ sr->probe_active_time = GNUNET_TIME_absolute_get ();
+ sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+ sr->remaining_probe_time,
+ &probe_failure_handler,
+ sr);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
+ sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
+ dur);
+ break;
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+ return sr;
+}
+
+
+/**
+ * Start download probes for the given search result.
+ *
+ * @param sr the search result
+ */
+static void
+start_probe (struct SearchResult *sr)
+{
+ uint64_t off;
+ uint64_t len;
+
+ if (sr->probe_ctx != NULL)
+ return;
+ if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
+ return;
+ if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
+ return;
+ len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
+ if (len == 0)
+ return;
+ if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
+ return;
+ off = len / DBLOCK_SIZE;
+ if (off > 0)
+ off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
+ off *= DBLOCK_SIZE;
+ if (len - off < DBLOCK_SIZE)
+ len = len - off;
+ else
+ len = DBLOCK_SIZE;
+ sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
+ 2 * (1 + sr->availability_trials));
+ sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
+ sr->uri,
+ sr->meta,
+ NULL, NULL,
+ off, len,
+ sr->sc->anonymity,
+ GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
+ GNUNET_FS_DOWNLOAD_IS_PROBE,
+ sr, NULL);
+}
+
+
/**
* We have received a KSK result. Check how it fits in with the
* overall query and notify the client accordingly.
if (NULL == sr)
{
sr = GNUNET_malloc (sizeof (struct SearchResult));
+ sr->sc = sc;
sr->uri = GNUNET_FS_uri_dup (uri);
sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
sr->mandatory_missing = sc->mandatory_count;
notify_client_chk_result (sc, sr);
else
notify_client_chk_update (sc, sr);
- /* FIXME: consider starting probes for "sr" */
+ start_probe (sr);
}
(void*) uri))
return; /* duplicate result */
sr = GNUNET_malloc (sizeof (struct SearchResult));
+ sr->sc = sc;
sr->uri = GNUNET_FS_uri_dup (uri);
sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
&key,
sr,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
- /* FIXME: consider starting probes for "sr" */
-
+ start_probe (sr);
/* notify client */
notify_client_chk_result (sc, sr);
/* search for updates */
parent->child_tail,
sc);
pi.status = GNUNET_FS_STATUS_SEARCH_START;
- make_search_status (&pi, sc);
- sc->client_info = h->upcb (h->upcb_cls,
- &pi);
+ sc->client_info = make_search_status (&pi, sc);
GNUNET_CLIENT_notify_transmit_ready (client,
size,
GNUNET_CONSTANTS_SERVICE_TIMEOUT,
// FIXME: make persistent!
// FIXME: should this freeze all active probes?
pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
- make_search_status (&pi, sc);
- sc->client_info = sc->h->upcb (sc->h->upcb_cls,
- &pi);
+ sc->client_info = make_search_status (&pi, sc);
}
do_reconnect (sc, NULL);
// FIXME: make persistent!
pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
- make_search_status (&pi, sc);
- sc->client_info = sc->h->upcb (sc->h->upcb_cls,
- &pi);
+ sc->client_info = make_search_status (&pi, sc);
}
struct GNUNET_FS_ProgressInfo pi;
pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
- make_search_status (&pi, sc);
pi.value.search.specifics.result_stopped.cctx = sr->client_info;
pi.value.search.specifics.result_stopped.meta = sr->meta;
pi.value.search.specifics.result_stopped.uri = sr->uri;
- sr->client_info = h->upcb (h->upcb_cls,
- &pi);
+ sr->client_info = make_search_status (&pi, sc);
GNUNET_break (NULL == sr->client_info);
GNUNET_FS_uri_destroy (sr->uri);
&search_result_free,
sc);
pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
- make_search_status (&pi, sc);
- sc->client_info = sc->h->upcb (sc->h->upcb_cls,
- &pi);
+ sc->client_info = make_search_status (&pi, sc);
GNUNET_break (NULL == sc->client_info);
if (sc->task != GNUNET_SCHEDULER_NO_TASK)
GNUNET_SCHEDULER_cancel (sc->h->sched,
* Is persistence of operations desired?
* (will create SUSPEND/RESUME events).
*/
- GNUNET_FS_FLAGS_PERSISTENCE = 1
+ GNUNET_FS_FLAGS_PERSISTENCE = 1,
+
+ /**
+ * Should we automatically trigger probes for search results
+ * to determine availability?
+ * (will create GNUNET_FS_STATUS_SEARCH_UPDATE events).
+ */
+ GNUNET_FS_FLAGS_DO_PROBES = 2
};
* Do not append temporary data to
* the target file (for the IBlocks).
*/
- GNUNET_FS_DOWNLOAD_NO_TEMPORARIES = 2
+ GNUNET_FS_DOWNLOAD_NO_TEMPORARIES = 2,
+
+ /**
+ * Internal option used to flag this download as a 'probe' for a
+ * search result. Impacts the priority with which the download is
+ * run and causes signalling callbacks to be done differently.
+ * Also, probe downloads are not serialized on suspension. Normal
+ * clients should not use this!
+ */
+ GNUNET_FS_DOWNLOAD_IS_PROBE = (1<<31)
};
GNUNET_TIME_Relative
a2);
+/**
+ * Subtract relative timestamp from the other.
+ *
+ * @param a1 first timestamp
+ * @param a2 second timestamp
+ * @return ZERO if a2>=a1 (including both FOREVER), FOREVER if a1 is FOREVER, a1-a2 otherwise
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_subtract (struct GNUNET_TIME_Relative a1,
+ struct GNUNET_TIME_Relative a2);
+
/**
* Convert relative time to network byte order.
/**
* Add relative times together.
*
+ * @param a1 first timestamp
+ * @param a2 second timestamp
* @return FOREVER if either argument is FOREVER or on overflow; a1+a2 otherwise
*/
struct GNUNET_TIME_Relative
}
+/**
+ * Subtract relative timestamp from the other.
+ *
+ * @param a1 first timestamp
+ * @param a2 second timestamp
+ * @return ZERO if a2>=a1 (including both FOREVER), FOREVER if a1 is FOREVER, a1-a2 otherwise
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_subtract (struct GNUNET_TIME_Relative a1,
+ struct GNUNET_TIME_Relative a2)
+{
+ struct GNUNET_TIME_Relative ret;
+
+ if (a2.value >= a1.value)
+ return GNUNET_TIME_relative_get_zero ();
+ if (a1.value == (uint64_t) - 1LL)
+ return GNUNET_TIME_relative_get_forever ();
+ ret.value = a1.value - a2.value;
+ return ret;
+}
+
+
/**
* Convert relative time to network byte order.
+ *
+ * @param a time to convert
+ * @return time in network byte order
*/
struct GNUNET_TIME_RelativeNBO
GNUNET_TIME_relative_hton (struct GNUNET_TIME_Relative a)
/**
* Convert relative time from network byte order.
+ *
+ * @param a time to convert
+ * @return time in host byte order
*/
struct GNUNET_TIME_Relative
GNUNET_TIME_relative_ntoh (struct GNUNET_TIME_RelativeNBO a)
/**
* Convert absolute time to network byte order.
+ *
+ * @param a time to convert
+ * @return time in network byte order
*/
struct GNUNET_TIME_AbsoluteNBO
GNUNET_TIME_absolute_hton (struct GNUNET_TIME_Absolute a)
/**
* Convert absolute time from network byte order.
+ *
+ * @param a time to convert
+ * @return time in host byte order
*/
struct GNUNET_TIME_Absolute
GNUNET_TIME_absolute_ntoh (struct GNUNET_TIME_AbsoluteNBO a)