#ifndef __DV_BUCKET_C__ #define __DV_BUCKET_C__ #include "dv_bucket.h" #define DV_1E6 1000000 struct timezone tzp_not_used; static inline double dv_bucket_timeval2double(struct timeval *tp) { return ((double) tp->tv_sec) * DV_1E6 + tp->tv_usec; } dv_bucket_item * dv_bucket_item_create(double time) { dv_bucket_item *item; item = (dv_bucket_item *) malloc( sizeof(dv_bucket_item) ); item->next = NULL; item->time = time; return item; } void dv_bucket_item_destroy(dv_bucket_item *item) { free(item); } dv_bucket* dv_bucket_create(double interval, unsigned long max, int strict_interval) { dv_bucket *bucket; bucket = (dv_bucket *) malloc( sizeof(dv_bucket)); bucket->max = max; bucket->interval = interval * DV_1E6; bucket->count = 0; bucket->strict_interval = strict_interval; bucket->head = NULL; bucket->tail = NULL; return bucket; } long dv_bucket_max_items(dv_bucket *bucket) { return bucket->max; } double dv_bucket_interval(dv_bucket *bucket) { double ret = bucket->interval / DV_1E6; return ret; } long dv_bucket_count(dv_bucket *bucket) { return bucket->count; } void dv_bucket_reset(dv_bucket *bucket) { dv_bucket_item *item = bucket->head; while (item) { dv_bucket_item *tmp = item->next; dv_bucket_item_destroy(item); item = tmp; } bucket->head = NULL; bucket->tail = NULL; bucket->count = 0; } void dv_bucket_destroy(dv_bucket *bucket) { dv_bucket_item *item = bucket->head; dv_bucket_item *tmp; while (item != NULL) { tmp = item->next; free(item); item = tmp; } free(bucket); } size_t dv_bucket_expire( dv_bucket *bucket, struct timeval *tp ) { /* get the difference from the head of the list and the * time we're currently trying to insert. * if the difference is bigger than the interval specified, * we can safely drop the oldest bucket. * we repeat until bucket->head is within the given interval */ size_t expired = 0; double dtime = dv_bucket_timeval2double(tp); while ( bucket->head != NULL && bucket->interval < dtime - bucket->head->time ) { dv_bucket_item *tmp = bucket->head; bucket->head = bucket->head->next; if (bucket->head == NULL) { bucket->tail = NULL; } dv_bucket_item_destroy(tmp); bucket->count--; expired++; } return expired; } int dv_bucket_is_full(dv_bucket *bucket, double dtime) { if (bucket->count == 0 || bucket->head == NULL) { /* safety net */ return 0; } /* if we're in strict_interval mode, then we check for the last entry * in the list, and make sure that current time is more than * last entry + interval */ if (bucket->strict_interval) { return bucket->head->time + bucket->interval > dtime; } /* Otherwise, we care about how many items are in the list */ return bucket->max <= bucket->count; } void dv_bucket_push(dv_bucket *bucket, double time) { dv_bucket_item *item = dv_bucket_item_create(time); if (bucket->count == 0) { bucket->head = item; bucket->tail = item; } else { bucket->tail->next = item; bucket->tail = item; } bucket->count++; } int dv_bucket_try_push(dv_bucket *bucket) { struct timeval t; double dtime; gettimeofday(&t, &tzp_not_used); dv_bucket_expire( bucket, &t ); dtime = dv_bucket_timeval2double(&t); if ( dv_bucket_count( bucket ) == 0 ) { dv_bucket_push( bucket, dtime ); return 1; } if ( dv_bucket_is_full(bucket, dtime) ) { return 0; } dv_bucket_push( bucket, dtime ); return 1; } SV * dv_bucket_serialize(dv_bucket *bucket) { SV *sv = newSVpv("[", 1); dv_bucket_item *item = bucket->head; while (item) { sv_catpvf(sv, "%f%s", item->time, item->next ? "," : ""); item = item->next; } sv_catpv(sv, "]"); return sv; } dv_bucket * dv_bucket_deserialize(char *buf, size_t len, double interval, unsigned long max, int strict_interval) { dv_bucket *bucket = dv_bucket_create(interval, max, strict_interval); char *end = buf + len; if (buf != end && *buf == '[') { buf++; while (buf != end && !isdigit(*buf)) { buf++; } } while (buf != end) { dv_bucket_push(bucket, strtod(buf, NULL)); /* pass through the number we just read */ while ( buf != end && (isdigit(*buf) || *buf == '.')) { buf++; } /* find the next number */ while ( buf != end && ! isdigit(*buf)) { buf++; } } return bucket; } dv_bucket_item * dv_bucket_first(dv_bucket *bucket) { return bucket->head; } dv_bucket_item * dv_bucket_item_next(dv_bucket_item *item) { return item->next; } double dv_bucket_item_time(dv_bucket_item *item) { return item->time; } #endif /* __DV_BUCKET_C__ */