X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/91c9324af56bd50592e818fd4763e6cdcea68857..4005a0d66ea1aea1475088c998d35f16ef800bba:/server/choose.c diff --git a/server/choose.c b/server/choose.c index f22bf6f..f06ed9e 100644 --- a/server/choose.c +++ b/server/choose.c @@ -53,6 +53,8 @@ #include "trackdb-int.h" #include "version.h" #include "trackname.h" +#include "queue.h" +#include "server-queue.h" static DB_TXN *global_tid; @@ -106,6 +108,16 @@ static long ntracks; static char **required_tags; static char **prohibited_tags; +static int queue_contains(const struct queue_entry *head, + const char *track) { + const struct queue_entry *q; + + for(q = head->next; q != head; q = q->next) + if(!strcmp(q->track, track)) + return 1; + return 0; +} + /** @brief Compute the weight of a track * @param track Track name (UTF-8) * @param data Track data @@ -141,10 +153,15 @@ static unsigned long compute_weight(const char *track, if((s = kvp_get(prefs, "played_time"))) { last = atoll(s); now = time(0); - if(now < last + 8 * 3600) /* TODO configurable */ + if(now < last + config->replay_min) return 0; } + /* Reject tracks currently in the queue or in the recent list */ + if(queue_contains(&qhead, track) + || queue_contains(&phead, track)) + return 0; + /* We'll need tags for a number of things */ track_tags = parsetags(kvp_get(prefs, "tags")); @@ -156,6 +173,16 @@ static unsigned long compute_weight(const char *track, if(*required_tags && !tag_intersection(track_tags, required_tags)) return 0; + /* Use the configured weight if available */ + if((s = kvp_get(prefs, "weight"))) { + long n; + errno = 0; + + n = strtol(s, 0, 10); + if((errno == 0 || errno == ERANGE) && n >= 0) + return n; + } + return 90000; } @@ -165,11 +192,15 @@ static int collect_tracks_callback(const char *track, struct kvp *prefs, void attribute((unused)) *u, DB_TXN attribute((unused)) *tid) { - const unsigned long weight = compute_weight(track, data, prefs); + unsigned long weight = compute_weight(track, data, prefs); if(weight) { struct weighted_track *const t = xmalloc(sizeof *t); + /* Clamp weight so that we can fit in billions of tracks when we do + * arithmetic in long long */ + if(weight > 0x7fffffff) + weight = 0x7fffffff; t->next = tracks; t->track = track; t->weight = weight; @@ -181,8 +212,7 @@ static int collect_tracks_callback(const char *track, } /** @brief Pick a random integer uniformly from [0, limit) */ -static unsigned long long pick_weight(unsigned long long limit) { - unsigned long long n; +static void random_bytes(unsigned char *buf, size_t n) { static int fd = -1; int r; @@ -190,11 +220,68 @@ static unsigned long long pick_weight(unsigned long long limit) { if((fd = open("/dev/urandom", O_RDONLY)) < 0) fatal(errno, "opening /dev/urandom"); } - if((r = read(fd, &n, sizeof n)) < 0) + if((r = read(fd, buf, n)) < 0) fatal(errno, "reading /dev/urandom"); - if((size_t)r < sizeof n) + if((size_t)r < n) fatal(0, "short read from /dev/urandom"); - return n % limit; +} + +/** @brief Pick a random integer uniformly from [0, limit) */ +static unsigned long long pick_weight(unsigned long long limit) { + unsigned char buf[(sizeof(unsigned long long) * CHAR_BIT + 7)/8], m; + unsigned long long t, r, slop; + int i, nby, nbi; + + //info("pick_weight: limit = %llu", limit); + + /* First, decide how many bits of output we actually need; do bytes first + * (they're quicker) and then bits. + * + * To speed this up, we could use a binary search if we knew where to + * start. (Note that shifting by ULLONG_BITS or more (if such a constant + * existed) is undefined behaviour, so we mustn't do that.) Figuring out a + * start point involves preprocessor and/or autoconf magic. + */ + for (nby = 1, t = (limit - 1) >> 8; t; nby++, t >>= 8) + ; + nbi = (nby - 1) << 3; t = limit >> nbi; + if (t >> 4) { t >>= 4; nbi += 4; } + if (t >> 2) { t >>= 2; nbi += 2; } + if (t >> 1) { t >>= 1; nbi += 1; } + nbi++; + //info("nby = %d; nbi = %d", nby, nbi); + + /* Main randomness collection loop. We read a number of bytes from the + * randomness source, and glue them together into an integer (dropping + * bits off the top byte as necessary). Call the result r; we have + * 2^{nbi - 1) <= limit < 2^nbi and r < 2^nbi. If r < limit then we win; + * otherwise we try again. Given the above bounds, we expect fewer than 2 + * iterations. + * + * Unfortunately there are subtleties. In particular, 2^nbi may in fact be + * zero due to overflow. So in fact what we do is compute slop = 2^nbi - + * limit > 0; if r < slop then we try again, otherwise r - slop is our + * winner. + */ + slop = (2 << (nbi - 1)) - limit; + m = nbi & 7 ? (1 << (nbi & 7)) - 1 : 0xff; + //info("slop = %llu", slop); + //info("m = 0x%02x", m); + + do { + /* Actually get some random data. */ + random_bytes(buf, nby); + + /* Clobber the top byte. */ + buf[0] &= m; + + /* Turn it into an integer. */ + for (r = 0, i = 0; i < nby; i++) + r = (r << 8) | buf[i]; + //info("r = %llu", r); + } while (r < slop); + + return r - slop; } /** @brief Pick a track at random and write it to stdout */ @@ -237,6 +324,9 @@ int main(int argc, char **argv) { log_default = &log_syslog; } if(config_read(0)) fatal(0, "cannot read configuration"); + /* Find out current queue/recent list */ + queue_read(); + recent_read(); /* Generate the candidate track list */ trackdb_init(TRACKDB_NO_RECOVER); trackdb_open(TRACKDB_NO_UPGRADE|TRACKDB_READ_ONLY);