1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
14 #include "trackerlogic.h"
20 static int vector_compare_peer6(const void *peer1
, const void *peer2
) { return memcmp(peer1
, peer2
, OT_PEER_COMPARE_SIZE6
); }
21 static int vector_compare_peer4(const void *peer1
, const void *peer2
) { return memcmp(peer1
, peer2
, OT_PEER_COMPARE_SIZE4
); }
23 /* This function gives us a binary search that returns a pointer, even if
24 no exact match is found. In that case it sets exactmatch 0 and gives
25 calling functions the chance to insert data
27 void *binary_search(const void *const key
, const void *base
, const size_t member_count
, const size_t member_size
, size_t compare_size
, int *exactmatch
) {
28 size_t interval
= member_count
;
31 uint8_t *lookat
= ((uint8_t *)base
) + member_size
* (interval
/ 2);
32 int cmp
= memcmp(lookat
, key
, compare_size
);
38 base
= lookat
+ member_size
;
44 *exactmatch
= interval
;
48 static uint8_t vector_hash_peer(ot_peer
const *peer
, size_t compare_size
, int bucket_count
) {
49 unsigned int hash
= 5381;
50 uint8_t *p
= (uint8_t *)peer
;
51 while (compare_size
--)
52 hash
+= (hash
<< 5) + *(p
++);
53 return hash
% bucket_count
;
56 /* This is the generic insert operation for our vector type.
57 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
58 those of objects in vector. Our special "binary_search" function does that and either returns the match or a
59 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
60 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
61 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
63 void *vector_find_or_insert(ot_vector
*vector
, void *key
, size_t member_size
, size_t compare_size
, int *exactmatch
) {
64 uint8_t *match
= binary_search(key
, vector
->data
, vector
->size
, member_size
, compare_size
, exactmatch
);
69 if (vector
->size
+ 1 > vector
->space
) {
70 size_t new_space
= vector
->space
? OT_VECTOR_GROW_RATIO
* vector
->space
: OT_VECTOR_MIN_MEMBERS
;
71 uint8_t *new_data
= realloc(vector
->data
, new_space
* member_size
);
74 /* Adjust pointer if it moved by realloc */
75 match
= new_data
+ (match
- (uint8_t *)vector
->data
);
77 vector
->data
= new_data
;
78 vector
->space
= new_space
;
80 memmove(match
+ member_size
, match
, ((uint8_t *)vector
->data
) + member_size
* vector
->size
- match
);
86 ot_peer
*vector_find_or_insert_peer(ot_vector
*vector
, ot_peer
const *peer
, size_t peer_size
, int *exactmatch
) {
88 const size_t compare_size
= OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size
);
91 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
92 if (vector
->space
< vector
->size
)
93 vector
= ((ot_vector
*)vector
->data
) + vector_hash_peer(peer
, compare_size
, vector
->size
);
94 match
= binary_search(peer
, vector
->data
, vector
->size
, peer_size
, compare_size
, exactmatch
);
99 /* This is the amount of bytes that needs to be pushed backwards by peer_size bytes to make room for new peer */
100 end
= (ot_peer
*)vector
->data
+ vector
->size
* peer_size
;
101 match_to_end
= end
- match
;
103 if (vector
->size
+ 1 > vector
->space
) {
104 ptrdiff_t offset
= match
- (ot_peer
*)vector
->data
;
105 size_t new_space
= vector
->space
? OT_VECTOR_GROW_RATIO
* vector
->space
: OT_VECTOR_MIN_MEMBERS
;
106 ot_peer
*new_data
= realloc(vector
->data
, new_space
* peer_size
);
110 /* Adjust pointer if it moved by realloc */
111 match
= new_data
+ offset
;
113 vector
->data
= new_data
;
114 vector
->space
= new_space
;
117 /* Here we're guaranteed to have enough space in vector to move the block of peers after insertion point */
118 memmove(match
+ peer_size
, match
, match_to_end
);
124 /* This is the non-generic delete from vector-operation specialized for peers in pools.
125 It returns 0 if no peer was found (and thus not removed)
126 1 if a non-seeding peer was removed
127 2 if a seeding peer was removed
129 int vector_remove_peer(ot_vector
*vector
, ot_peer
const *peer
, size_t peer_size
) {
130 int exactmatch
, was_seeder
;
131 ot_peer
*match
, *end
;
132 size_t compare_size
= OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size
);
137 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
138 if (vector
->space
< vector
->size
)
139 vector
= ((ot_vector
*)vector
->data
) + vector_hash_peer(peer
, compare_size
, vector
->size
);
141 end
= ((ot_peer
*)vector
->data
) + peer_size
* vector
->size
;
142 match
= (ot_peer
*)binary_search(peer
, vector
->data
, vector
->size
, peer_size
, compare_size
, &exactmatch
);
146 was_seeder
= (OT_PEERFLAG_D(match
, peer_size
) & PEER_FLAG_SEEDING
) ? 2 : 1;
147 memmove(match
, match
+ peer_size
, end
- match
- peer_size
);
150 vector_fixup_peers(vector
, peer_size
);
154 void vector_remove_torrent(ot_vector
*vector
, ot_torrent
*match
) {
155 ot_torrent
*end
= ((ot_torrent
*)vector
->data
) + vector
->size
;
160 /* If this is being called after a unsuccessful malloc() for peer_list
161 in add_peer_to_torrent, match->peer_list actually might be NULL */
162 free_peerlist(match
->peer_list6
);
163 free_peerlist(match
->peer_list4
);
165 memmove(match
, match
+ 1, sizeof(ot_torrent
) * (end
- match
- 1));
166 if ((--vector
->size
* OT_VECTOR_SHRINK_THRESH
< vector
->space
) && (vector
->space
>= OT_VECTOR_SHRINK_RATIO
* OT_VECTOR_MIN_MEMBERS
)) {
167 vector
->space
/= OT_VECTOR_SHRINK_RATIO
;
168 vector
->data
= realloc(vector
->data
, vector
->space
* sizeof(ot_torrent
));
172 void vector_clean_list(ot_vector
*vector
, int num_buckets
) {
173 while (num_buckets
--)
174 free(vector
[num_buckets
].data
);
179 void vector_redistribute_buckets(ot_peerlist
*peer_list
, size_t peer_size
) {
180 int tmp
, bucket
, bucket_size_new
, num_buckets_new
, num_buckets_old
= 1;
181 ot_vector
*bucket_list_new
, *bucket_list_old
= &peer_list
->peers
;
182 int (*sort_func
)(const void *, const void *) = peer_size
== OT_PEER_SIZE6
? &vector_compare_peer6
: &vector_compare_peer4
;
184 if (OT_PEERLIST_HASBUCKETS(peer_list
)) {
185 num_buckets_old
= peer_list
->peers
.size
;
186 bucket_list_old
= peer_list
->peers
.data
;
189 if (peer_list
->peer_count
< 255)
191 else if (peer_list
->peer_count
> 8192)
192 num_buckets_new
= 64;
193 else if (peer_list
->peer_count
>= 512 && peer_list
->peer_count
< 4096)
194 num_buckets_new
= 16;
195 else if (peer_list
->peer_count
< 512 && num_buckets_old
<= 16)
196 num_buckets_new
= num_buckets_old
;
197 else if (peer_list
->peer_count
< 512)
199 else if (peer_list
->peer_count
< 8192 && num_buckets_old
> 1)
200 num_buckets_new
= num_buckets_old
;
202 num_buckets_new
= 16;
204 if (num_buckets_new
== num_buckets_old
)
207 /* Assume near perfect distribution */
208 bucket_list_new
= malloc(num_buckets_new
* sizeof(ot_vector
));
209 if (!bucket_list_new
)
211 bzero(bucket_list_new
, num_buckets_new
* sizeof(ot_vector
));
213 tmp
= peer_list
->peer_count
/ num_buckets_new
;
214 bucket_size_new
= OT_VECTOR_MIN_MEMBERS
;
215 while (bucket_size_new
< tmp
)
216 bucket_size_new
*= OT_VECTOR_GROW_RATIO
;
218 /* preallocate vectors to hold all peers */
219 for (bucket
= 0; bucket
< num_buckets_new
; ++bucket
) {
220 bucket_list_new
[bucket
].space
= bucket_size_new
;
221 bucket_list_new
[bucket
].data
= malloc(bucket_size_new
* peer_size
);
222 if (!bucket_list_new
[bucket
].data
)
223 return vector_clean_list(bucket_list_new
, num_buckets_new
);
226 /* Now sort them into the correct bucket */
227 for (bucket
= 0; bucket
< num_buckets_old
; ++bucket
) {
228 ot_peer
*peers_old
= bucket_list_old
[bucket
].data
;
229 int peer_count_old
= bucket_list_old
[bucket
].size
;
230 while (peer_count_old
--) {
231 ot_vector
*bucket_dest
= bucket_list_new
;
232 if (num_buckets_new
> 1)
233 bucket_dest
+= vector_hash_peer(peers_old
, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size
), num_buckets_new
);
234 if (bucket_dest
->size
+ 1 > bucket_dest
->space
) {
235 void *tmp
= realloc(bucket_dest
->data
, peer_size
* OT_VECTOR_GROW_RATIO
* bucket_dest
->space
);
237 return vector_clean_list(bucket_list_new
, num_buckets_new
);
238 bucket_dest
->data
= tmp
;
239 bucket_dest
->space
*= OT_VECTOR_GROW_RATIO
;
241 memcpy((ot_peer
*)bucket_dest
->data
+ peer_size
* bucket_dest
->size
++, peers_old
, peer_size
);
242 peers_old
+= peer_size
;
246 /* Now sort each bucket to later allow bsearch */
247 for (bucket
= 0; bucket
< num_buckets_new
; ++bucket
)
248 qsort(bucket_list_new
[bucket
].data
, bucket_list_new
[bucket
].size
, peer_size
, sort_func
);
250 /* Everything worked fine. Now link new bucket_list to peer_list */
251 if (OT_PEERLIST_HASBUCKETS(peer_list
))
252 vector_clean_list((ot_vector
*)peer_list
->peers
.data
, peer_list
->peers
.size
);
254 free(peer_list
->peers
.data
);
256 if (num_buckets_new
> 1) {
257 peer_list
->peers
.data
= bucket_list_new
;
258 peer_list
->peers
.size
= num_buckets_new
;
259 peer_list
->peers
.space
= 0; /* Magic marker for "is list of buckets" */
261 peer_list
->peers
.data
= bucket_list_new
->data
;
262 peer_list
->peers
.size
= bucket_list_new
->size
;
263 peer_list
->peers
.space
= bucket_list_new
->space
;
264 free(bucket_list_new
);
268 void vector_fixup_peers(ot_vector
*vector
, size_t peer_size
) {
278 while ((vector
->size
* OT_VECTOR_SHRINK_THRESH
< vector
->space
) && (vector
->space
>= OT_VECTOR_SHRINK_RATIO
* OT_VECTOR_MIN_MEMBERS
)) {
279 vector
->space
/= OT_VECTOR_SHRINK_RATIO
;
283 vector
->data
= realloc(vector
->data
, vector
->space
* peer_size
);