address linter warnings. Thanks to gagath@debian.org
[opentracker.git] / ot_vector.c
blob2bc07b5fb0de29c048f2849abf4a02848585423e
1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
4 $id$ */
6 /* System */
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <strings.h>
13 /* Opentracker */
14 #include "trackerlogic.h"
16 /* Libowfat */
17 #include "uint16.h"
18 #include "uint32.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;
30 while (interval) {
31 uint8_t *lookat = ((uint8_t *)base) + member_size * (interval / 2);
32 int cmp = memcmp(lookat, key, compare_size);
33 if (cmp == 0) {
34 base = lookat;
35 break;
37 if (cmp < 0) {
38 base = lookat + member_size;
39 interval--;
41 interval /= 2;
44 *exactmatch = interval;
45 return (void *)base;
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);
66 if (*exactmatch)
67 return match;
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);
72 if (!new_data)
73 return NULL;
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);
82 vector->size++;
83 return match;
86 ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch) {
87 ot_peer *match, *end;
88 const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
89 size_t match_to_end;
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);
96 if (*exactmatch)
97 return match;
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);
108 if (!new_data)
109 return NULL;
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);
120 vector->size++;
121 return match;
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);
134 if (!vector->size)
135 return 0;
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);
143 if (!exactmatch)
144 return 0;
146 was_seeder = (OT_PEERFLAG_D(match, peer_size) & PEER_FLAG_SEEDING) ? 2 : 1;
147 memmove(match, match + peer_size, end - match - peer_size);
149 vector->size--;
150 vector_fixup_peers(vector, peer_size);
151 return was_seeder;
154 void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
155 ot_torrent *end = ((ot_torrent *)vector->data) + vector->size;
157 if (!vector->size)
158 return;
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);
175 free(vector);
176 return;
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)
190 num_buckets_new = 1;
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)
198 num_buckets_new = 1;
199 else if (peer_list->peer_count < 8192 && num_buckets_old > 1)
200 num_buckets_new = num_buckets_old;
201 else
202 num_buckets_new = 16;
204 if (num_buckets_new == num_buckets_old)
205 return;
207 /* Assume near perfect distribution */
208 bucket_list_new = malloc(num_buckets_new * sizeof(ot_vector));
209 if (!bucket_list_new)
210 return;
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);
236 if (!tmp)
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);
253 else
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" */
260 } else {
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) {
269 int need_fix = 0;
271 if (!vector->size) {
272 free(vector->data);
273 vector->data = NULL;
274 vector->space = 0;
275 return;
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;
280 need_fix++;
282 if (need_fix)
283 vector->data = realloc(vector->data, vector->space * peer_size);