summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ot_clean.c64
-rw-r--r--src/ot_fullscrape.c4
-rw-r--r--src/ot_stats.c14
-rw-r--r--src/ot_vector.c47
-rw-r--r--src/ot_vector.h2
-rw-r--r--src/trackerlogic.c118
-rw-r--r--src/trackerlogic.h39
7 files changed, 150 insertions, 138 deletions
diff --git a/src/ot_clean.c b/src/ot_clean.c
index 9e2186f..130bd2a 100644
--- a/src/ot_clean.c
+++ b/src/ot_clean.c
@@ -51,51 +51,48 @@ static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t pee
51 return (peers - insert_point) / peer_size; 51 return (peers - insert_point) / peer_size;
52} 52}
53 53
54/* Age one family's peers by timedout minutes. Frees the list and clears the 54/* Age one family's peers by timedout minutes. Releases and zeroes the vector
55 slot once its last peer is gone, so an absent family costs nothing. */ 55 once its last peer is gone, so an empty family costs nothing. */
56static void clean_peer_list_family(ot_peerlist **slot, size_t peer_size, ot_time timedout) { 56static void clean_family_vector(ot_vector *peer_vector, size_t peer_size, ot_time timedout, size_t *removed_seeders) {
57 ot_peerlist *peer_list = *slot; 57 ot_vector *bucket = peer_vector;
58 ot_vector *peer_vector; 58 size_t num_buckets = 1, peer_count;
59 size_t num_buckets = 1, removed_seeders = 0;
60 59
61 if (!peer_list) 60 if (OT_VECTOR_HASBUCKETS(peer_vector)) {
62 return;
63
64 peer_vector = &peer_list->peers;
65 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
66 num_buckets = peer_vector->size; 61 num_buckets = peer_vector->size;
67 peer_vector = (ot_vector *)peer_vector->data; 62 bucket = (ot_vector *)peer_vector->data;
68 } 63 }
69 64
70 while (num_buckets--) { 65 while (num_buckets--) {
71 size_t removed_peers = clean_single_bucket(peer_vector->data, peer_vector->size, peer_size, timedout, &removed_seeders); 66 size_t removed_peers = clean_single_bucket(bucket->data, bucket->size, peer_size, timedout, removed_seeders);
72 peer_list->peer_count -= removed_peers; 67 bucket->size -= removed_peers;
73 peer_vector->size -= removed_peers;
74 if (removed_peers) 68 if (removed_peers)
75 vector_fixup_peers(peer_vector, peer_size); 69 vector_fixup_peers(bucket, peer_size);
76 70
77 /* Skip to next bucket, a vector containing peers */ 71 /* Skip to next bucket, a vector containing peers */
78 ++peer_vector; 72 ++bucket;
79 } 73 }
80 74
81 peer_list->seed_count -= removed_seeders; 75 peer_count = vector_peer_count(peer_vector);
82 76
83 if (!peer_list->peer_count) { 77 /* Run dry: release the family (a collapsed bucket list or a plain vector) */
84 free_peerlist(peer_list); 78 if (!peer_count) {
85 *slot = NULL; 79 free_peer_vector(peer_vector);
80 memset(peer_vector, 0, sizeof(*peer_vector));
86 return; 81 return;
87 } 82 }
88 83
89 /* See if we need to convert the list from simple vector to bucket list */ 84 /* See if we need to convert the vector from simple to bucket list or back */
90 if ((peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT) || OT_PEERLIST_HASBUCKETS(peer_list)) 85 if ((peer_count > OT_PEER_BUCKET_MINCOUNT) || OT_VECTOR_HASBUCKETS(peer_vector))
91 vector_redistribute_buckets(peer_list, peer_size); 86 vector_redistribute_buckets(peer_vector, peer_size, peer_count);
92} 87}
93 88
94/* Clean a single torrent 89/* Clean a single torrent
95 return 1 if torrent timed out 90 return 1 if torrent timed out
96*/ 91*/
97int clean_single_torrent(ot_torrent *torrent) { 92int clean_single_torrent(ot_torrent *torrent) {
98 ot_time timedout = time_since_minutes(torrent->base); 93 ot_time timedout = time_since_minutes(torrent->base);
94 ot_peerlist *peer_list = torrent->peer_list;
95 size_t removed_seeders = 0;
99 96
100 /* Touched since the last sweep: nothing to do */ 97 /* Touched since the last sweep: nothing to do */
101 if (!timedout) 98 if (!timedout)
@@ -107,7 +104,7 @@ int clean_single_torrent(ot_torrent *torrent) {
107 104
108 /* Already empty and nothing arrived before the peer timeout: keep it only 105 /* Already empty and nothing arrived before the peer timeout: keep it only
109 while a down count is worth remembering */ 106 while a down count is worth remembering */
110 if (timedout > OT_PEER_TIMEOUT && !torrent->peer_list6 && !torrent->peer_list4) 107 if (timedout > OT_PEER_TIMEOUT && !peer_list)
111 return (torrent->down_count && timedout <= g_empty_torrent_timeout) ? 0 : 1; 108 return (torrent->down_count && timedout <= g_empty_torrent_timeout) ? 0 : 1;
112 109
113 /* Peers past the peer timeout are gone regardless, which caps how far the 110 /* Peers past the peer timeout are gone regardless, which caps how far the
@@ -115,10 +112,19 @@ int clean_single_torrent(ot_torrent *torrent) {
115 if (timedout > OT_PEER_TIMEOUT) 112 if (timedout > OT_PEER_TIMEOUT)
116 timedout = OT_PEER_TIMEOUT; 113 timedout = OT_PEER_TIMEOUT;
117 114
118 clean_peer_list_family(&torrent->peer_list6, OT_PEER_SIZE6, timedout); 115 if (peer_list) {
119 clean_peer_list_family(&torrent->peer_list4, OT_PEER_SIZE4, timedout); 116 clean_family_vector(&peer_list->peers6, OT_PEER_SIZE6, timedout, &removed_seeders);
117 clean_family_vector(&peer_list->peers4, OT_PEER_SIZE4, timedout, &removed_seeders);
118 peer_list->seed_count -= removed_seeders;
119
120 /* Both families gone: drop the peerlist, the torrent keeps only its count */
121 if (!vector_peer_count(&peer_list->peers6) && !vector_peer_count(&peer_list->peers4)) {
122 free_peerlist(peer_list);
123 torrent->peer_list = peer_list = NULL;
124 }
125 }
120 126
121 if (torrent->peer_list6 || torrent->peer_list4) 127 if (peer_list)
122 torrent->base = time_now_minutes(); /* still carrying peers */ 128 torrent->base = time_now_minutes(); /* still carrying peers */
123 else { 129 else {
124 /* Fell empty: hold base a peer timeout back so a later idle sweep makes 130 /* Fell empty: hold base a peer timeout back so a later idle sweep makes
diff --git a/src/ot_fullscrape.c b/src/ot_fullscrape.c
index 9fae444..787b508 100644
--- a/src/ot_fullscrape.c
+++ b/src/ot_fullscrape.c
@@ -90,8 +90,8 @@ static void fullscrape_write_one( ot_tasktype mode, char **cursor, ot_torrent *t
90 exactly that size */ 90 exactly that size */
91 char *r = *cursor; 91 char *r = *cursor;
92 const char *r_end = r + OT_SCRAPE_MAXENTRYLEN; 92 const char *r_end = r + OT_SCRAPE_MAXENTRYLEN;
93 size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 93 size_t seed_count = torrent_seed_count(torrent);
94 size_t peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); 94 size_t peer_count = torrent_peer_count(torrent);
95 size_t down_count = torrent->down_count; 95 size_t down_count = torrent->down_count;
96 96
97 switch (mode & TASK_TASK_MASK) { 97 switch (mode & TASK_TASK_MASK) {
diff --git a/src/ot_stats.c b/src/ot_stats.c
index 9e2eabe..0e2fc67 100644
--- a/src/ot_stats.c
+++ b/src/ot_stats.c
@@ -243,15 +243,15 @@ static void stats_slash24s_txt(char **reply, const char *reply_end, size_t amoun
243 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 243 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
244 ot_vector *torrents_list = mutex_bucket_lock(bucket); 244 ot_vector *torrents_list = mutex_bucket_lock(bucket);
245 for (i = 0; i < torrents_list->size; ++i) { 245 for (i = 0; i < torrents_list->size; ++i) {
246 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[i]).peer_list4; 246 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[i]).peer_list;
247 ot_vector *bucket_list; 247 ot_vector *bucket_list;
248 int num_buckets = 1; 248 int num_buckets = 1;
249 249
250 if (!peer_list) 250 if (!peer_list)
251 continue; 251 continue;
252 bucket_list = &peer_list->peers; 252 bucket_list = &peer_list->peers4; /* /24 networks are an IPv4 notion */
253 253
254 if (OT_PEERLIST_HASBUCKETS(peer_list)) { 254 if (OT_VECTOR_HASBUCKETS(bucket_list)) {
255 num_buckets = bucket_list->size; 255 num_buckets = bucket_list->size;
256 bucket_list = (ot_vector *)bucket_list->data; 256 bucket_list = (ot_vector *)bucket_list->data;
257 } 257 }
@@ -303,8 +303,8 @@ typedef struct {
303static int torrent_statter(ot_torrent *torrent, uintptr_t data) { 303static int torrent_statter(ot_torrent *torrent, uintptr_t data) {
304 torrent_stats *stats = (torrent_stats *)data; 304 torrent_stats *stats = (torrent_stats *)data;
305 stats->torrent_count++; 305 stats->torrent_count++;
306 stats->peer_count += peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); 306 stats->peer_count += torrent_peer_count(torrent);
307 stats->seed_count += peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 307 stats->seed_count += torrent_seed_count(torrent);
308 return 0; 308 return 0;
309} 309}
310 310
@@ -347,8 +347,8 @@ void stats_top_txt(char **reply, const char *reply_end, size_t amount) {
347 ot_vector *torrents_list = mutex_bucket_lock(bucket); 347 ot_vector *torrents_list = mutex_bucket_lock(bucket);
348 for (j = 0; j < torrents_list->size; ++j) { 348 for (j = 0; j < torrents_list->size; ++j) {
349 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j; 349 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j;
350 size_t peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); 350 size_t peer_count = torrent_peer_count(torrent);
351 size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 351 size_t seed_count = torrent_seed_count(torrent);
352 size_t leech_count = peer_count - seed_count; 352 size_t leech_count = peer_count - seed_count;
353 353
354 stats_insert_into_highscore(peer_count, top100c, amount, &torrent->hash); 354 stats_insert_into_highscore(peer_count, top100c, amount, &torrent->hash);
diff --git a/src/ot_vector.c b/src/ot_vector.c
index 23dfb10..ef38919 100644
--- a/src/ot_vector.c
+++ b/src/ot_vector.c
@@ -160,8 +160,7 @@ void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
160 160
161 end = ((ot_torrent *)vector->data) + vector->size; 161 end = ((ot_torrent *)vector->data) + vector->size;
162 162
163 free_peerlist(match->peer_list6); 163 free_peerlist(match->peer_list);
164 free_peerlist(match->peer_list4);
165 164
166 memmove(match, match + 1, sizeof(ot_torrent) * (end - match - 1)); 165 memmove(match, match + 1, sizeof(ot_torrent) * (end - match - 1));
167 if ((--vector->size * OT_VECTOR_SHRINK_THRESH < vector->space) && (vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS)) { 166 if ((--vector->size * OT_VECTOR_SHRINK_THRESH < vector->space) && (vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS)) {
@@ -180,27 +179,27 @@ void vector_clean_list(ot_vector *vector, size_t num_buckets) {
180 return; 179 return;
181} 180}
182 181
183void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) { 182void vector_redistribute_buckets(ot_vector *peer_vector, size_t peer_size, size_t peer_count) {
184 size_t bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1; 183 size_t bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1;
185 ot_vector *bucket_list_new, *bucket_list_old = &peer_list->peers; 184 ot_vector *bucket_list_new, *bucket_list_old = peer_vector;
186 int (*sort_func)(const void *, const void *) = peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4; 185 int (*sort_func)(const void *, const void *) = peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4;
187 186
188 if (OT_PEERLIST_HASBUCKETS(peer_list)) { 187 if (OT_VECTOR_HASBUCKETS(peer_vector)) {
189 num_buckets_old = peer_list->peers.size; 188 num_buckets_old = peer_vector->size;
190 bucket_list_old = peer_list->peers.data; 189 bucket_list_old = peer_vector->data;
191 } 190 }
192 191
193 if (peer_list->peer_count < 255) 192 if (peer_count < 255)
194 num_buckets_new = 1; 193 num_buckets_new = 1;
195 else if (peer_list->peer_count > 8192) 194 else if (peer_count > 8192)
196 num_buckets_new = 64; 195 num_buckets_new = 64;
197 else if (peer_list->peer_count >= 512 && peer_list->peer_count < 4096) 196 else if (peer_count >= 512 && peer_count < 4096)
198 num_buckets_new = 16; 197 num_buckets_new = 16;
199 else if (peer_list->peer_count < 512 && num_buckets_old <= 16) 198 else if (peer_count < 512 && num_buckets_old <= 16)
200 num_buckets_new = num_buckets_old; 199 num_buckets_new = num_buckets_old;
201 else if (peer_list->peer_count < 512) 200 else if (peer_count < 512)
202 num_buckets_new = 1; 201 num_buckets_new = 1;
203 else if (peer_list->peer_count < 8192 && num_buckets_old > 1) 202 else if (peer_count < 8192 && num_buckets_old > 1)
204 num_buckets_new = num_buckets_old; 203 num_buckets_new = num_buckets_old;
205 else 204 else
206 num_buckets_new = 16; 205 num_buckets_new = 16;
@@ -214,7 +213,7 @@ void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) {
214 return; 213 return;
215 214
216 bucket_size_new = OT_VECTOR_MIN_MEMBERS; 215 bucket_size_new = OT_VECTOR_MIN_MEMBERS;
217 while (bucket_size_new * num_buckets_new < peer_list->peer_count) 216 while (bucket_size_new * num_buckets_new < peer_count)
218 bucket_size_new *= OT_VECTOR_GROW_RATIO; 217 bucket_size_new *= OT_VECTOR_GROW_RATIO;
219 218
220 /* preallocate vectors to hold all peers */ 219 /* preallocate vectors to hold all peers */
@@ -253,20 +252,20 @@ void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) {
253 for (bucket = 0; bucket < num_buckets_new; ++bucket) 252 for (bucket = 0; bucket < num_buckets_new; ++bucket)
254 qsort(bucket_list_new[bucket].data, bucket_list_new[bucket].size, peer_size, sort_func); 253 qsort(bucket_list_new[bucket].data, bucket_list_new[bucket].size, peer_size, sort_func);
255 254
256 /* Everything worked fine. Now link new bucket_list to peer_list */ 255 /* Everything worked fine. Now link new bucket_list to the vector */
257 if (OT_PEERLIST_HASBUCKETS(peer_list)) 256 if (OT_VECTOR_HASBUCKETS(peer_vector))
258 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size); 257 vector_clean_list((ot_vector *)peer_vector->data, peer_vector->size);
259 else 258 else
260 free(peer_list->peers.data); 259 free(peer_vector->data);
261 260
262 if (num_buckets_new > 1) { 261 if (num_buckets_new > 1) {
263 peer_list->peers.data = bucket_list_new; 262 peer_vector->data = bucket_list_new;
264 peer_list->peers.size = num_buckets_new; 263 peer_vector->size = num_buckets_new;
265 peer_list->peers.space = 0; /* Magic marker for "is list of buckets" */ 264 peer_vector->space = 0; /* Magic marker for "is list of buckets" */
266 } else { 265 } else {
267 peer_list->peers.data = bucket_list_new->data; 266 peer_vector->data = bucket_list_new->data;
268 peer_list->peers.size = bucket_list_new->size; 267 peer_vector->size = bucket_list_new->size;
269 peer_list->peers.space = bucket_list_new->space; 268 peer_vector->space = bucket_list_new->space;
270 free(bucket_list_new); 269 free(bucket_list_new);
271 } 270 }
272} 271}
diff --git a/src/ot_vector.h b/src/ot_vector.h
index 8a5ba86..1a73fcf 100644
--- a/src/ot_vector.h
+++ b/src/ot_vector.h
@@ -32,7 +32,7 @@ int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_
32void vector_remove_torrent(ot_vector *vector, ot_torrent *match); 32void vector_remove_torrent(ot_vector *vector, ot_torrent *match);
33 33
34/* For ot_clean.c */ 34/* For ot_clean.c */
35void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size); 35void vector_redistribute_buckets(ot_vector *peer_vector, size_t peer_size, size_t peer_count);
36void vector_fixup_peers(ot_vector *vector, size_t peer_size); 36void vector_fixup_peers(ot_vector *vector, size_t peer_size);
37void vector_clean_list(ot_vector *vector, size_t num_buckets); 37void vector_clean_list(ot_vector *vector, size_t num_buckets);
38 38
diff --git a/src/trackerlogic.c b/src/trackerlogic.c
index 6b0659c..4ba3a41 100644
--- a/src/trackerlogic.c
+++ b/src/trackerlogic.c
@@ -28,13 +28,18 @@
28/* Forward declaration */ 28/* Forward declaration */
29size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto); 29size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
30 30
31void free_peer_vector(ot_vector *peer_vector) {
32 if (OT_VECTOR_HASBUCKETS(peer_vector))
33 vector_clean_list((ot_vector *)peer_vector->data, peer_vector->size);
34 else
35 free(peer_vector->data); /* free(NULL) on an absent family is fine */
36}
37
31void free_peerlist(ot_peerlist *peer_list) { 38void free_peerlist(ot_peerlist *peer_list) {
32 if (peer_list && peer_list->peers.data) { 39 if (!peer_list)
33 if (OT_PEERLIST_HASBUCKETS(peer_list)) 40 return;
34 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size); 41 free_peer_vector(&peer_list->peers6);
35 else 42 free_peer_vector(&peer_list->peers4);
36 free(peer_list->peers.data);
37 }
38 free(peer_list); 43 free(peer_list);
39} 44}
40 45
@@ -113,20 +118,17 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru
113 118
114 torrent->base = time_now_minutes(); 119 torrent->base = time_now_minutes();
115 120
116 /* The announcing peer's family list is created on first use */ 121 /* The peerlist, holding both families, is created on the first peer */
117 { 122 if (!torrent->peer_list && !(torrent->peer_list = calloc(1, sizeof(ot_peerlist)))) {
118 ot_peerlist **slot = peer_size == OT_PEER_SIZE6 ? &torrent->peer_list6 : &torrent->peer_list4; 123 if (delta_torrentcount)
119 if (!*slot && !(*slot = calloc(1, sizeof(ot_peerlist)))) { 124 vector_remove_torrent(torrents_list, torrent);
120 if (delta_torrentcount) 125 mutex_bucket_unlock_by_hash(*ws->hash, 0);
121 vector_remove_torrent(torrents_list, torrent); 126 return 0;
122 mutex_bucket_unlock_by_hash(*ws->hash, 0);
123 return 0;
124 }
125 peer_list = *slot;
126 } 127 }
128 peer_list = torrent->peer_list;
127 129
128 /* Check for peer in torrent */ 130 /* Check for peer in its family's vector */
129 peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer_src, peer_size, &exactmatch); 131 peer_dest = vector_find_or_insert_peer(peer_size == OT_PEER_SIZE6 ? &peer_list->peers6 : &peer_list->peers4, peer_src, peer_size, &exactmatch);
130 if (!peer_dest) { 132 if (!peer_dest) {
131 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount); 133 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
132 return 0; 134 return 0;
@@ -149,7 +151,6 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru
149 livesync_tell(ws); 151 livesync_tell(ws);
150#endif 152#endif
151 153
152 peer_list->peer_count++;
153 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { 154 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) {
154 torrent->down_count++; 155 torrent->down_count++;
155 stats_issue_event(ws, EVENT_COMPLETED, 0, 0); 156 stats_issue_event(ws, EVENT_COMPLETED, 0, 0);
@@ -198,14 +199,14 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru
198 return ws->reply_size; 199 return ws->reply_size;
199} 200}
200 201
201static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *reply) { 202static size_t return_peers_all(ot_vector *peer_vector, size_t peer_size, size_t peer_count, char *reply) {
202 size_t bucket, num_buckets = 1; 203 size_t bucket, num_buckets = 1;
203 ot_vector *bucket_list = &peer_list->peers; 204 ot_vector *bucket_list = peer_vector;
204 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size); 205 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
205 size_t result = compare_size * peer_list->peer_count; 206 size_t result = compare_size * peer_count;
206 char *r_end = reply + result; 207 char *r_end = reply + result;
207 208
208 if (OT_PEERLIST_HASBUCKETS(peer_list)) { 209 if (OT_VECTOR_HASBUCKETS(peer_vector)) {
209 num_buckets = bucket_list->size; 210 num_buckets = bucket_list->size;
210 bucket_list = (ot_vector *)bucket_list->data; 211 bucket_list = (ot_vector *)bucket_list->data;
211 } 212 }
@@ -227,17 +228,17 @@ static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *r
227 return result; 228 return result;
228} 229}
229 230
230static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) { 231static size_t return_peers_selection(struct ot_workstruct *ws, ot_vector *peer_vector, size_t peer_size, size_t amount, size_t peer_count, char *reply) {
231 size_t bucket_offset, bucket_index = 0, num_buckets = 1; 232 size_t bucket_offset, bucket_index = 0, num_buckets = 1;
232 ot_vector *bucket_list = &peer_list->peers; 233 ot_vector *bucket_list = peer_vector;
233 unsigned int shifted_pc = peer_list->peer_count; 234 unsigned int shifted_pc = peer_count;
234 unsigned int shifted_step = 0; 235 unsigned int shifted_step = 0;
235 unsigned int shift = 0; 236 unsigned int shift = 0;
236 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size); 237 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
237 size_t result = compare_size * amount; 238 size_t result = compare_size * amount;
238 char *r_end = reply + result; 239 char *r_end = reply + result;
239 240
240 if (OT_PEERLIST_HASBUCKETS(peer_list)) { 241 if (OT_VECTOR_HASBUCKETS(peer_vector)) {
241 num_buckets = bucket_list->size; 242 num_buckets = bucket_list->size;
242 bucket_list = (ot_vector *)bucket_list->data; 243 bucket_list = (ot_vector *)bucket_list->data;
243 } 244 }
@@ -253,7 +254,7 @@ static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer
253 254
254 /* Initialize somewhere in the middle of peers so that 255 /* Initialize somewhere in the middle of peers so that
255 fixpoint's aliasing doesn't alway miss the same peers */ 256 fixpoint's aliasing doesn't alway miss the same peers */
256 bucket_offset = nrand48(ws->rand48_state) % peer_list->peer_count; 257 bucket_offset = nrand48(ws->rand48_state) % peer_count;
257 258
258 while (amount--) { 259 while (amount--) {
259 ot_peer *peer; 260 ot_peer *peer;
@@ -281,10 +282,11 @@ static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer
281static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) { 282static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
282 char *r = reply; 283 char *r = reply;
283 size_t peer_size = peer_size_from_peer6(&ws->peer); 284 size_t peer_size = peer_size_from_peer6(&ws->peer);
284 ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; 285 ot_peerlist *peer_list = torrent->peer_list;
285 size_t list_count = peerlist_peer_count(peer_list); 286 ot_vector *family = peer_size == OT_PEER_SIZE6 ? &peer_list->peers6 : &peer_list->peers4;
286 size_t peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); 287 size_t list_count = vector_peer_count(family);
287 size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 288 size_t peer_count = torrent_peer_count(torrent);
289 size_t seed_count = torrent_seed_count(torrent);
288 290
289 if (amount > list_count) 291 if (amount > list_count)
290 amount = list_count; 292 amount = list_count;
@@ -296,9 +298,9 @@ static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent
296 298
297 if (amount) { 299 if (amount) {
298 if (amount == list_count) 300 if (amount == list_count)
299 r += return_peers_all(peer_list, peer_size, r); 301 r += return_peers_all(family, peer_size, list_count, r);
300 else 302 else
301 r += return_peers_selection(ws, peer_list, peer_size, amount, r); 303 r += return_peers_selection(ws, family, peer_size, amount, list_count, r);
302 } 304 }
303 return r - reply; 305 return r - reply;
304} 306}
@@ -307,9 +309,10 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent
307 char *r = reply; 309 char *r = reply;
308 const char *r_end = ws->outbuf + G_OUTBUF_SIZE; 310 const char *r_end = ws->outbuf + G_OUTBUF_SIZE;
309 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; 311 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
310 size_t count6 = peerlist_peer_count(torrent->peer_list6); 312 ot_peerlist *peer_list = torrent->peer_list;
311 size_t count4 = peerlist_peer_count(torrent->peer_list4); 313 size_t count6 = vector_peer_count(&peer_list->peers6);
312 size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 314 size_t count4 = vector_peer_count(&peer_list->peers4);
315 size_t seed_count = peer_list->seed_count;
313 size_t down_count = torrent->down_count; 316 size_t down_count = torrent->down_count;
314 size_t peer_count = count6 + count4 - seed_count; 317 size_t peer_count = count6 + count4 - seed_count;
315 318
@@ -355,17 +358,17 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent
355 if (amount_v4) { 358 if (amount_v4) {
356 fmt_append(&r, r_end, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4); 359 fmt_append(&r, r_end, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4);
357 if (amount_v4 == count4) 360 if (amount_v4 == count4)
358 r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r); 361 r += return_peers_all(&peer_list->peers4, OT_PEER_SIZE4, count4, r);
359 else 362 else
360 r += return_peers_selection(ws, torrent->peer_list4, OT_PEER_SIZE4, amount_v4, r); 363 r += return_peers_selection(ws, &peer_list->peers4, OT_PEER_SIZE4, amount_v4, count4, r);
361 } 364 }
362 365
363 if (amount_v6) { 366 if (amount_v6) {
364 fmt_append(&r, r_end, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6); 367 fmt_append(&r, r_end, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6);
365 if (amount_v6 == count6) 368 if (amount_v6 == count6)
366 r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r); 369 r += return_peers_all(&peer_list->peers6, OT_PEER_SIZE6, count6, r);
367 else 370 else
368 r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r); 371 r += return_peers_selection(ws, &peer_list->peers6, OT_PEER_SIZE6, amount_v6, count6, r);
369 } 372 }
370 373
371 fmt_append(&r, r_end, "e"); 374 fmt_append(&r, r_end, "e");
@@ -401,9 +404,9 @@ size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) {
401 memset(reply, 0, 12); 404 memset(reply, 0, 12);
402 delta_torrentcount = -1; 405 delta_torrentcount = -1;
403 } else { 406 } else {
404 r[0] = htonl(peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4)); 407 r[0] = htonl(torrent_seed_count(torrent));
405 r[1] = htonl(torrent->down_count); 408 r[1] = htonl(torrent->down_count);
406 r[2] = htonl(peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4) - peerlist_seed_count(torrent->peer_list6) - peerlist_seed_count(torrent->peer_list4)); 409 r[2] = htonl(torrent_peer_count(torrent) - torrent_seed_count(torrent));
407 } 410 }
408 } 411 }
409 mutex_bucket_unlock_by_hash(hash, delta_torrentcount); 412 mutex_bucket_unlock_by_hash(hash, delta_torrentcount);
@@ -430,8 +433,8 @@ void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char
430 } else { 433 } else {
431 fmt_append(reply, reply_end, "20:"); 434 fmt_append(reply, reply_end, "20:");
432 fmt_bytes(reply, reply_end, hash, sizeof(ot_hash)); 435 fmt_bytes(reply, reply_end, hash, sizeof(ot_hash));
433 size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 436 size_t seed_count = torrent_seed_count(torrent);
434 size_t peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); 437 size_t peer_count = torrent_peer_count(torrent);
435 fmt_append(reply, reply_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, (size_t)torrent->down_count, peer_count - seed_count); 438 fmt_append(reply, reply_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, (size_t)torrent->down_count, peer_count - seed_count);
436 } 439 }
437 } 440 }
@@ -441,12 +444,10 @@ void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char
441 fmt_append(reply, reply_end, "ee"); 444 fmt_append(reply, reply_end, "ee");
442} 445}
443 446
444static ot_peerlist dummy_list;
445size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) { 447size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
446 int exactmatch; 448 int exactmatch;
447 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash); 449 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
448 ot_torrent *torrent = vector_binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); 450 ot_torrent *torrent = vector_binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
449 ot_peerlist *peer_list = &dummy_list;
450 size_t peer_size; /* initialized in next line */ 451 size_t peer_size; /* initialized in next line */
451 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size); 452 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
452 size_t peer_count = 0, seed_count = 0; 453 size_t peer_count = 0, seed_count = 0;
@@ -458,21 +459,13 @@ size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
458 } 459 }
459#endif 460#endif
460 461
461 if (exactmatch) { 462 if (exactmatch && torrent->peer_list) {
462 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; 463 ot_vector *family = peer_size == OT_PEER_SIZE6 ? &torrent->peer_list->peers6 : &torrent->peer_list->peers4;
463 if (peer_list) { 464 if (vector_remove_peer(family, peer_src, peer_size) == 2)
464 switch (vector_remove_peer(&peer_list->peers, peer_src, peer_size)) { 465 torrent->peer_list->seed_count--;
465 case 2:
466 peer_list->seed_count--; /* Intentional fallthrough */
467 case 1:
468 peer_list->peer_count--; /* Intentional fallthrough */
469 default:
470 break;
471 }
472 }
473 466
474 peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); 467 peer_count = torrent_peer_count(torrent);
475 seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); 468 seed_count = torrent_seed_count(torrent);
476 } 469 }
477 470
478 if (proto == FLAG_TCP) { 471 if (proto == FLAG_TCP) {
@@ -621,8 +614,7 @@ void trackerlogic_deinit(void) {
621 if (torrents_list->size) { 614 if (torrents_list->size) {
622 for (j = 0; j < torrents_list->size; ++j) { 615 for (j = 0; j < torrents_list->size; ++j) {
623 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + j; 616 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + j;
624 free_peerlist(torrent->peer_list6); 617 free_peerlist(torrent->peer_list);
625 free_peerlist(torrent->peer_list4);
626 delta_torrentcount -= 1; 618 delta_torrentcount -= 1;
627 } 619 }
628 free(torrents_list->data); 620 free(torrents_list->data);
diff --git a/src/trackerlogic.h b/src/trackerlogic.h
index c4b7774..dd8a48c 100644
--- a/src/trackerlogic.h
+++ b/src/trackerlogic.h
@@ -170,26 +170,40 @@ struct ot_peerlist;
170typedef struct ot_peerlist ot_peerlist; 170typedef struct ot_peerlist ot_peerlist;
171typedef struct { 171typedef struct {
172 ot_hash hash; 172 ot_hash hash;
173 ot_peerlist *peer_list6; /* NULL until the torrent has a v6 peer */ 173 ot_peerlist *peer_list; /* NULL until the torrent has a peer in either family */
174 ot_peerlist *peer_list4; /* NULL until the torrent has a v4 peer */ 174 uint32_t base; /* epoch minutes, when the torrent was last cleaned */
175 uint32_t base; /* minutes, when the torrent was last cleaned */ 175 uint32_t down_count; /* completed events */
176 uint32_t down_count; /* completed events, summed over both families */
177} ot_torrent; 176} ot_torrent;
178 177
179#include "ot_vector.h" 178#include "ot_vector.h"
180 179
181struct ot_peerlist { 180struct ot_peerlist {
182 uint32_t seed_count; 181 uint32_t seed_count;
183 uint32_t peer_count; 182 /* each family's peers: a normal vector, or a pointer to ot_vector[] buckets
184 /* normal peers vector or 183 when data != NULL and space == 0 (size then holds the bucket count) */
185 pointer to ot_vector[32] buckets if data != NULL and space == 0 184 ot_vector peers6;
186 */ 185 ot_vector peers4;
187 ot_vector peers;
188}; 186};
189#define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list)->peers.size > (peer_list)->peers.space)
190 187
191static inline uint32_t peerlist_peer_count(const ot_peerlist *peer_list) { return peer_list ? peer_list->peer_count : 0; } 188/* A peer vector becomes a bucket list of peer vectors, marker is size > space (usually 0) */
192static inline uint32_t peerlist_seed_count(const ot_peerlist *peer_list) { return peer_list ? peer_list->seed_count : 0; } 189#define OT_VECTOR_HASBUCKETS(v) ((v)->size > (v)->space)
190
191/* Peers in one family: a plain vector's size is the count, a bucket list's is
192 the sum of its buckets. O(1) for the small torrents that dominate a table. */
193static inline size_t vector_peer_count(const ot_vector *v) {
194 const ot_vector *b;
195 size_t n, total = 0;
196 if (!OT_VECTOR_HASBUCKETS(v))
197 return v->size;
198 b = (const ot_vector *)v->data;
199 for (n = 0; n < v->size; ++n)
200 total += b[n].size;
201 return total;
202}
203static inline size_t torrent_peer_count(const ot_torrent *t) {
204 return t->peer_list ? vector_peer_count(&t->peer_list->peers6) + vector_peer_count(&t->peer_list->peers4) : 0;
205}
206static inline uint32_t torrent_seed_count(const ot_torrent *t) { return t->peer_list ? t->peer_list->seed_count : 0; }
193 207
194/* The http replies the tracker counts when it has to refuse a request. */ 208/* The http replies the tracker counts when it has to refuse a request. */
195enum { 209enum {
@@ -314,5 +328,6 @@ void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data),
314 328
315/* Helper, before it moves to its own object */ 329/* Helper, before it moves to its own object */
316void free_peerlist(ot_peerlist *peer_list); 330void free_peerlist(ot_peerlist *peer_list);
331void free_peer_vector(ot_vector *peer_vector);
317 332
318#endif 333#endif