You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
opentracker/ot_vector.c

285 lines
10 KiB
C

/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
It is considered beerware. Prost. Skol. Cheers or whatever.
16 years ago
$id$ */
/* System */
5 months ago
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
/* Opentracker */
#include "trackerlogic.h"
/* Libowfat */
#include "uint16.h"
5 months ago
#include "uint32.h"
5 months ago
static int vector_compare_peer6(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE6); }
static int vector_compare_peer4(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE4); }
/* This function gives us a binary search that returns a pointer, even if
no exact match is found. In that case it sets exactmatch 0 and gives
calling functions the chance to insert data
*/
5 months ago
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) {
size_t interval = member_count;
5 months ago
while (interval) {
uint8_t *lookat = ((uint8_t *)base) + member_size * (interval / 2);
int cmp = memcmp(lookat, key, compare_size);
if (cmp == 0) {
base = lookat;
break;
}
5 months ago
if (cmp < 0) {
base = lookat + member_size;
interval--;
}
interval /= 2;
}
16 years ago
*exactmatch = interval;
5 months ago
return (void *)base;
}
5 months ago
static uint8_t vector_hash_peer(ot_peer const *peer, size_t compare_size, int bucket_count) {
unsigned int hash = 5381;
5 months ago
uint8_t *p = (uint8_t *)peer;
while (compare_size--)
hash += (hash << 5) + *(p++);
return hash % bucket_count;
}
/* This is the generic insert operation for our vector type.
It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
those of objects in vector. Our special "binary_search" function does that and either returns the match or a
pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
*/
5 months ago
void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch) {
uint8_t *match = binary_search(key, vector->data, vector->size, member_size, compare_size, exactmatch);
5 months ago
if (*exactmatch)
return match;
5 months ago
if (vector->size + 1 > vector->space) {
size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
5 months ago
uint8_t *new_data = realloc(vector->data, new_space * member_size);
if (!new_data)
return NULL;
/* Adjust pointer if it moved by realloc */
5 months ago
match = new_data + (match - (uint8_t *)vector->data);
5 months ago
vector->data = new_data;
vector->space = new_space;
}
5 months ago
memmove(match + member_size, match, ((uint8_t *)vector->data) + member_size * vector->size - match);
vector->size++;
return match;
}
5 months ago
ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch) {
ot_peer *match, *end;
const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
5 months ago
size_t match_to_end;
/* If space is zero but size is set, we're dealing with a list of vector->size buckets */
5 months ago
if (vector->space < vector->size)
vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size);
match = binary_search(peer, vector->data, vector->size, peer_size, compare_size, exactmatch);
5 months ago
if (*exactmatch)
return match;
16 years ago
/* This is the amount of bytes that needs to be pushed backwards by peer_size bytes to make room for new peer */
5 months ago
end = (ot_peer *)vector->data + vector->size * peer_size;
match_to_end = end - match;
5 months ago
if (vector->size + 1 > vector->space) {
ptrdiff_t offset = match - (ot_peer *)vector->data;
size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
ot_peer *new_data = realloc(vector->data, new_space * peer_size);
5 months ago
if (!new_data)
return NULL;
/* Adjust pointer if it moved by realloc */
5 months ago
match = new_data + offset;
16 years ago
5 months ago
vector->data = new_data;
vector->space = new_space;
}
/* Here we're guaranteed to have enough space in vector to move the block of peers after insertion point */
5 months ago
memmove(match + peer_size, match, match_to_end);
16 years ago
vector->size++;
return match;
}
/* This is the non-generic delete from vector-operation specialized for peers in pools.
It returns 0 if no peer was found (and thus not removed)
1 if a non-seeding peer was removed
2 if a seeding peer was removed
*/
5 months ago
int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size) {
int exactmatch, was_seeder;
5 months ago
ot_peer *match, *end;
size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
5 months ago
if (!vector->size)
return 0;
16 years ago
/* If space is zero but size is set, we're dealing with a list of vector->size buckets */
5 months ago
if (vector->space < vector->size)
vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size);
5 months ago
end = ((ot_peer *)vector->data) + peer_size * vector->size;
match = (ot_peer *)binary_search(peer, vector->data, vector->size, peer_size, compare_size, &exactmatch);
if (!exactmatch)
return 0;
5 months ago
was_seeder = (OT_PEERFLAG_D(match, peer_size) & PEER_FLAG_SEEDING) ? 2 : 1;
memmove(match, match + peer_size, end - match - peer_size);
vector->size--;
5 months ago
vector_fixup_peers(vector, peer_size);
return was_seeder;
}
5 months ago
void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
ot_torrent *end = ((ot_torrent *)vector->data) + vector->size;
5 months ago
if (!vector->size)
return;
/* If this is being called after a unsuccessful malloc() for peer_list
in add_peer_to_torrent, match->peer_list actually might be NULL */
5 months ago
free_peerlist(match->peer_list6);
free_peerlist(match->peer_list4);
5 months ago
memmove(match, match + 1, sizeof(ot_torrent) * (end - match - 1));
if ((--vector->size * OT_VECTOR_SHRINK_THRESH < vector->space) && (vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS)) {
vector->space /= OT_VECTOR_SHRINK_RATIO;
5 months ago
vector->data = realloc(vector->data, vector->space * sizeof(ot_torrent));
}
}
5 months ago
void vector_clean_list(ot_vector *vector, int num_buckets) {
while (num_buckets--)
free(vector[num_buckets].data);
free(vector);
return;
}
5 months ago
void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) {
int tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1;
ot_vector *bucket_list_new, *bucket_list_old = &peer_list->peers;
int (*sort_func)(const void *, const void *) = peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4;
5 months ago
if (OT_PEERLIST_HASBUCKETS(peer_list)) {
num_buckets_old = peer_list->peers.size;
bucket_list_old = peer_list->peers.data;
}
5 months ago
if (peer_list->peer_count < 255)
num_buckets_new = 1;
5 months ago
else if (peer_list->peer_count > 8192)
num_buckets_new = 64;
5 months ago
else if (peer_list->peer_count >= 512 && peer_list->peer_count < 4096)
num_buckets_new = 16;
5 months ago
else if (peer_list->peer_count < 512 && num_buckets_old <= 16)
num_buckets_new = num_buckets_old;
5 months ago
else if (peer_list->peer_count < 512)
num_buckets_new = 1;
5 months ago
else if (peer_list->peer_count < 8192 && num_buckets_old > 1)
num_buckets_new = num_buckets_old;
else
num_buckets_new = 16;
5 months ago
if (num_buckets_new == num_buckets_old)
return;
/* Assume near perfect distribution */
5 months ago
bucket_list_new = malloc(num_buckets_new * sizeof(ot_vector));
if (!bucket_list_new)
return;
bzero(bucket_list_new, num_buckets_new * sizeof(ot_vector));
5 months ago
tmp = peer_list->peer_count / num_buckets_new;
bucket_size_new = OT_VECTOR_MIN_MEMBERS;
5 months ago
while (bucket_size_new < tmp)
16 years ago
bucket_size_new *= OT_VECTOR_GROW_RATIO;
/* preallocate vectors to hold all peers */
5 months ago
for (bucket = 0; bucket < num_buckets_new; ++bucket) {
bucket_list_new[bucket].space = bucket_size_new;
5 months ago
bucket_list_new[bucket].data = malloc(bucket_size_new * peer_size);
if (!bucket_list_new[bucket].data)
return vector_clean_list(bucket_list_new, num_buckets_new);
}
/* Now sort them into the correct bucket */
5 months ago
for (bucket = 0; bucket < num_buckets_old; ++bucket) {
ot_peer *peers_old = bucket_list_old[bucket].data;
int peer_count_old = bucket_list_old[bucket].size;
while (peer_count_old--) {
ot_vector *bucket_dest = bucket_list_new;
if (num_buckets_new > 1)
bucket_dest += vector_hash_peer(peers_old, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size), num_buckets_new);
5 months ago
if (bucket_dest->size + 1 > bucket_dest->space) {
void *tmp = realloc(bucket_dest->data, peer_size * OT_VECTOR_GROW_RATIO * bucket_dest->space);
if (!tmp)
return vector_clean_list(bucket_list_new, num_buckets_new);
bucket_dest->data = tmp;
bucket_dest->space *= OT_VECTOR_GROW_RATIO;
}
5 months ago
memcpy((ot_peer *)bucket_dest->data + peer_size * bucket_dest->size++, peers_old, peer_size);
peers_old += peer_size;
}
}
/* Now sort each bucket to later allow bsearch */
5 months ago
for (bucket = 0; bucket < num_buckets_new; ++bucket)
qsort(bucket_list_new[bucket].data, bucket_list_new[bucket].size, peer_size, sort_func);
/* Everything worked fine. Now link new bucket_list to peer_list */
5 months ago
if (OT_PEERLIST_HASBUCKETS(peer_list))
vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
else
5 months ago
free(peer_list->peers.data);
16 years ago
5 months ago
if (num_buckets_new > 1) {
peer_list->peers.data = bucket_list_new;
peer_list->peers.size = num_buckets_new;
peer_list->peers.space = 0; /* Magic marker for "is list of buckets" */
} else {
peer_list->peers.data = bucket_list_new->data;
peer_list->peers.size = bucket_list_new->size;
peer_list->peers.space = bucket_list_new->space;
5 months ago
free(bucket_list_new);
}
}
5 months ago
void vector_fixup_peers(ot_vector *vector, size_t peer_size) {
int need_fix = 0;
5 months ago
if (!vector->size) {
free(vector->data);
vector->data = NULL;
vector->space = 0;
return;
}
16 years ago
5 months ago
while ((vector->size * OT_VECTOR_SHRINK_THRESH < vector->space) && (vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS)) {
vector->space /= OT_VECTOR_SHRINK_RATIO;
need_fix++;
}
5 months ago
if (need_fix)
vector->data = realloc(vector->data, vector->space * peer_size);
}