Make chunked transfers use gzip also
[opentracker.git] / ot_vector.c
blob479e8320d110abddd6ca82d2be8b7498748e3f3f
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 <stdlib.h>
8 #include <string.h>
9 #include <strings.h>
10 #include <stdint.h>
11 #include <stddef.h>
13 /* Opentracker */
14 #include "trackerlogic.h"
15 #include "ot_vector.h"
17 /* Libowfat */
18 #include "uint32.h"
19 #include "uint16.h"
21 static int vector_compare_peer6(const void *peer1, const void *peer2 ) {
22 return memcmp( peer1, peer2, OT_PEER_COMPARE_SIZE6 );
24 static int vector_compare_peer4(const void *peer1, const void *peer2 ) {
25 return memcmp( peer1, peer2, OT_PEER_COMPARE_SIZE4 );
28 /* This function gives us a binary search that returns a pointer, even if
29 no exact match is found. In that case it sets exactmatch 0 and gives
30 calling functions the chance to insert data
32 void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size,
33 size_t compare_size, int *exactmatch ) {
34 size_t interval = member_count;
36 while( interval ) {
37 uint8_t *lookat = ((uint8_t*)base) + member_size * ( interval / 2 );
38 int cmp = memcmp( lookat, key, compare_size );
39 if(cmp == 0 ) {
40 base = lookat;
41 break;
43 if(cmp < 0) {
44 base = lookat + member_size;
45 interval--;
47 interval /= 2;
50 *exactmatch = interval;
51 return (void*)base;
54 static uint8_t vector_hash_peer( ot_peer const *peer, size_t compare_size, int bucket_count ) {
55 unsigned int hash = 5381;
56 uint8_t *p = (uint8_t*)peer;
57 while( compare_size-- ) hash += (hash<<5) + *(p++);
58 return hash % bucket_count;
61 /* This is the generic insert operation for our vector type.
62 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
63 those of objects in vector. Our special "binary_search" function does that and either returns the match or a
64 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
65 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
66 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
68 void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ) {
69 uint8_t *match = binary_search( key, vector->data, vector->size, member_size, compare_size, exactmatch );
71 if( *exactmatch ) return match;
73 if( vector->size + 1 > vector->space ) {
74 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
75 uint8_t *new_data = realloc( vector->data, new_space * member_size );
76 if( !new_data ) return NULL;
77 /* Adjust pointer if it moved by realloc */
78 match = new_data + (match - (uint8_t*)vector->data);
80 vector->data = new_data;
81 vector->space = new_space;
83 memmove( match + member_size, match, ((uint8_t*)vector->data) + member_size * vector->size - match );
85 vector->size++;
86 return match;
89 ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch ) {
90 ot_peer *match, *end;
91 const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
92 size_t match_to_end;
94 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
95 if( vector->space < vector->size )
96 vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, compare_size, vector->size );
97 match = binary_search( peer, vector->data, vector->size, peer_size, compare_size, exactmatch );
99 if( *exactmatch ) return match;
101 /* This is the amount of bytes that needs to be pushed backwards by peer_size bytes to make room for new peer */
102 end = (ot_peer*)vector->data + vector->size * peer_size;
103 match_to_end = end - match;
105 if( vector->size + 1 > vector->space ) {
106 ptrdiff_t offset = match - (ot_peer*)vector->data;
107 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
108 ot_peer *new_data = realloc( vector->data, new_space * peer_size );
110 if( !new_data ) return NULL;
111 /* Adjust pointer if it moved by realloc */
112 match = new_data + offset;
114 vector->data = new_data;
115 vector->space = new_space;
118 /* Here we're guaranteed to have enough space in vector to move the block of peers after insertion point */
119 memmove( match + peer_size, match, match_to_end);
121 vector->size++;
122 return match;
125 /* This is the non-generic delete from vector-operation specialized for peers in pools.
126 It returns 0 if no peer was found (and thus not removed)
127 1 if a non-seeding peer was removed
128 2 if a seeding peer was removed
130 int vector_remove_peer( ot_vector *vector, ot_peer const *peer, size_t peer_size) {
131 int exactmatch, was_seeder;
132 ot_peer *match, *end;
133 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
135 if( !vector->size ) 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 ) return 0;
145 was_seeder = ( OT_PEERFLAG_D( match, peer_size ) & PEER_FLAG_SEEDING ) ? 2 : 1;
146 memmove( match, match + peer_size, end - match - peer_size );
148 vector->size--;
149 vector_fixup_peers( vector, peer_size );
150 return was_seeder;
153 void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
154 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size;
156 if( !vector->size ) return;
158 /* If this is being called after a unsuccessful malloc() for peer_list
159 in add_peer_to_torrent, match->peer_list actually might be NULL */
160 free_peerlist( match->peer_list6 );
161 free_peerlist( match->peer_list4 );
163 memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) );
164 if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS ) ) {
165 vector->space /= OT_VECTOR_SHRINK_RATIO;
166 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) );
170 void vector_clean_list( ot_vector * vector, int num_buckets ) {
171 while( num_buckets-- )
172 free( vector[num_buckets].data );
173 free( vector );
174 return;
177 void vector_redistribute_buckets( ot_peerlist * peer_list, size_t peer_size ) {
178 int tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1;
179 ot_vector * bucket_list_new, * bucket_list_old = &peer_list->peers;
180 int (*sort_func)(const void *, const void *) =
181 peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4;
183 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) {
184 num_buckets_old = peer_list->peers.size;
185 bucket_list_old = peer_list->peers.data;
188 if( peer_list->peer_count < 255 )
189 num_buckets_new = 1;
190 else if( peer_list->peer_count > 8192 )
191 num_buckets_new = 64;
192 else if( peer_list->peer_count >= 512 && peer_list->peer_count < 4096 )
193 num_buckets_new = 16;
194 else if( peer_list->peer_count < 512 && num_buckets_old <= 16 )
195 num_buckets_new = num_buckets_old;
196 else if( peer_list->peer_count < 512 )
197 num_buckets_new = 1;
198 else if( peer_list->peer_count < 8192 && num_buckets_old > 1 )
199 num_buckets_new = num_buckets_old;
200 else
201 num_buckets_new = 16;
203 if( num_buckets_new == num_buckets_old )
204 return;
206 /* Assume near perfect distribution */
207 bucket_list_new = malloc( num_buckets_new * sizeof( ot_vector ) );
208 if( !bucket_list_new) return;
209 bzero( bucket_list_new, num_buckets_new * sizeof( ot_vector ) );
211 tmp = peer_list->peer_count / num_buckets_new;
212 bucket_size_new = OT_VECTOR_MIN_MEMBERS;
213 while( bucket_size_new < tmp)
214 bucket_size_new *= OT_VECTOR_GROW_RATIO;
216 /* preallocate vectors to hold all peers */
217 for( bucket=0; bucket<num_buckets_new; ++bucket ) {
218 bucket_list_new[bucket].space = bucket_size_new;
219 bucket_list_new[bucket].data = malloc( bucket_size_new * peer_size );
220 if( !bucket_list_new[bucket].data )
221 return vector_clean_list( bucket_list_new, num_buckets_new );
224 /* Now sort them into the correct bucket */
225 for( bucket=0; bucket<num_buckets_old; ++bucket ) {
226 ot_peer * peers_old = bucket_list_old[bucket].data;
227 int peer_count_old = bucket_list_old[bucket].size;
228 while( peer_count_old-- ) {
229 ot_vector * bucket_dest = bucket_list_new;
230 if( num_buckets_new > 1 )
231 bucket_dest += vector_hash_peer(peers_old, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size), num_buckets_new);
232 if( bucket_dest->size + 1 > bucket_dest->space ) {
233 void * tmp = realloc( bucket_dest->data, peer_size * OT_VECTOR_GROW_RATIO * bucket_dest->space );
234 if( !tmp ) return vector_clean_list( bucket_list_new, num_buckets_new );
235 bucket_dest->data = tmp;
236 bucket_dest->space *= OT_VECTOR_GROW_RATIO;
238 memcpy((ot_peer*)bucket_dest->data + peer_size * bucket_dest->size++, peers_old, peer_size);
239 peers_old += peer_size;
243 /* Now sort each bucket to later allow bsearch */
244 for( bucket=0; bucket<num_buckets_new; ++bucket )
245 qsort( bucket_list_new[bucket].data, bucket_list_new[bucket].size, peer_size, sort_func );
247 /* Everything worked fine. Now link new bucket_list to peer_list */
248 if( OT_PEERLIST_HASBUCKETS( peer_list) )
249 vector_clean_list( (ot_vector*)peer_list->peers.data, peer_list->peers.size );
250 else
251 free( peer_list->peers.data );
253 if( num_buckets_new > 1 ) {
254 peer_list->peers.data = bucket_list_new;
255 peer_list->peers.size = num_buckets_new;
256 peer_list->peers.space = 0; /* Magic marker for "is list of buckets" */
257 } else {
258 peer_list->peers.data = bucket_list_new->data;
259 peer_list->peers.size = bucket_list_new->size;
260 peer_list->peers.space = bucket_list_new->space;
261 free( bucket_list_new );
265 void vector_fixup_peers( ot_vector * vector, size_t peer_size ) {
266 int need_fix = 0;
268 if( !vector->size ) {
269 free( vector->data );
270 vector->data = NULL;
271 vector->space = 0;
272 return;
275 while( ( vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) &&
276 ( vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS ) ) {
277 vector->space /= OT_VECTOR_SHRINK_RATIO;
278 need_fix++;
280 if( need_fix )
281 vector->data = realloc( vector->data, vector->space * peer_size );
284 const char *g_version_vector_c = "$Source$: $Revision$\n";