Пример #1
0
/**
 * Insert `key' into the list.
 *
 * It is safe to call this routine whilst iterating although there is no
 * guarantee as to whether the iteration will see the new item.
 */
void
hash_list_insert_sorted(hash_list_t *hl, const void *key, cmp_fn_t func)
{
	link_t *lk;

	hash_list_check(hl);
	g_assert(NULL != func);
	g_assert(!hikset_contains(hl->ht, key));

	for (lk = elist_first(&hl->list); lk != NULL; lk = elist_next(lk)) {
		struct hash_list_item *item = ITEM(lk);
		if ((*func)(key, item->key) <= 0)
			break;
	}

	if (NULL == lk) {
		hash_list_append(hl, key);
	} else {
		struct hash_list_item *item;

		WALLOC(item);
		item->key = key;

		/* Inserting ``item'' before ``lk'' */

		elist_link_insert_before(&hl->list, lk, &item->lnk);
		hash_list_insert_item(hl, item);
	}
}
Пример #2
0
/**
 * Replace a key/value pair in the table.
 *
 * If the key already existed, the old key/values are replaced by the new ones,
 * at the same position.  Otherwise, the key is appended.
 *
 * @return TRUE when replacement occurred (the key existed).
 */
bool
ohash_table_replace(ohash_table_t *oh, const void *key, const void *value)
{
	struct ohash_pair pk;
	struct ohash_pair *op;
	const void *hkey;
	void *pos = NULL;

	ohash_table_check(oh);

	pk.oh = oh;
	pk.key = key;

	if (hash_list_find(oh->hl, &pk, &hkey)) {
		op = deconstify_pointer(hkey);
		g_assert(op->oh == oh);
		pos = hash_list_remove_position(oh->hl, &pk);
	} else {
		WALLOC(op);
		op->oh = oh;
		op->key = key;
	}

	op->value = value;
	if (pos != NULL) {
		hash_list_insert_position(oh->hl, op, pos);
	} else {
		hash_list_append(oh->hl, op);
	}

	return pos != NULL;
}
Пример #3
0
/**
 * Make sure the filename associated to a SHA1 is given the name of
 * the shared file and no longer bears the name of the partial file.
 * This can happen when the partial file is seeded then the file is
 * renamed and shared.
 */
void
upload_stats_enforce_local_filename(const shared_file_t *sf)
{
	struct ul_stats *s;
	const struct sha1 *sha1;
	const char *name;

	if (!upload_stats_by_sha1)
		return;		/* Nothing known by SHA1 yet */

	sha1 = sha1_hash_available(sf) ? shared_file_sha1(sf) : NULL;

	if (!sha1)
		return;		/* File's SHA1 not known yet, nothing to do here */

	s = g_hash_table_lookup(upload_stats_by_sha1, sha1);

	if (NULL == s)
		return;							/* SHA1 not in stats, nothing to do */

	name = shared_file_name_nfc(sf);
	if (name == s->filename)			/* Both are string atoms */
		return;							/* Everything is fine */

	/*
	 * We need to update the filename to match the shared file.
	 */

	hash_list_remove(upload_stats_list, s);
	atom_str_change(&s->pathname, shared_file_path(sf));
	atom_str_change(&s->filename, name);
	hash_list_append(upload_stats_list, s);

	gcu_upload_stats_gui_update_name(s);
}
Пример #4
0
static void
upload_stats_add(const char *pathname, filesize_t size, const char *name,
	guint32 attempts, guint32 complete, guint64 ul_bytes,
	time_t rtime, time_t dtime, const struct sha1 *sha1)
{
	static const struct ul_stats zero_stats;
	struct ul_stats *s;

	g_assert(pathname != NULL);
	g_assert(name != NULL);

	WALLOC(s);
	*s = zero_stats;
	s->pathname = atom_str_get(pathname);
	s->filename = atom_str_get(name);
	s->size = size;
	s->attempts = attempts;
	s->complete = complete;
	s->norm = size > 0 ? 1.0 * ul_bytes / size : 0.0;
	s->bytes_sent = ul_bytes;
	s->rtime = rtime;
	s->dtime = dtime;
	s->sha1 = sha1 ? atom_sha1_get(sha1) : NULL;

	if (!upload_stats_list) {
		g_assert(!upload_stats_by_sha1);
		upload_stats_list = hash_list_new(ul_stats_hash, ul_stats_eq);
		upload_stats_by_sha1 = g_hash_table_new(sha1_hash, sha1_eq);
	}
	hash_list_append(upload_stats_list, s);
	if (s->sha1)
		gm_hash_table_insert_const(upload_stats_by_sha1, s->sha1, s);
	gcu_upload_stats_gui_add(s);
}
Пример #5
0
/**
 * Record a waiting event.
 *
 * @param key		waiting key
 * @param cb		callback to invoke on wakeup
 * @param arg		additional callback argument
 *
 * @return the registered event, whose reference must be kept if it is meant
 * to be cancelled.
 */
wq_event_t *
wq_sleep(const void *key, wq_callback_t cb, void *arg)
{
	wq_event_t *we;
	hash_list_t *hl;

	we = wq_event_alloc(key, cb, arg);
	hl = htable_lookup(waitqueue, key);
	if (NULL == hl) {
		hl = hash_list_new(pointer_hash, NULL);
		htable_insert(waitqueue, key, hl);
	}
	hash_list_append(hl, we);		/* FIFO layout */

	return we;
}
Пример #6
0
/**
 * Allocate a new entry in the cache to hold the deserialized value.
 *
 * @param dw		the DBM wrapper
 * @param key		key we want a cache entry for
 * @param filled	optionally, a new cache entry already filled with the data
 *
 * @attention
 * An older cache entry structure can be returned, and it will still
 * point to the previous data.  Caller should normally invoke fill_entry()
 * immediately to make sure these stale data are not associated wrongly
 * with the new key, or supply his own filled structure directly.
 *
 * @return a cache entry object that can be filled with the value.
 */
static struct cached *
allocate_entry(dbmw_t *dw, gconstpointer key, struct cached *filled)
{
	struct cached *entry;
	gpointer saved_key;

	g_assert(!hash_list_contains(dw->keys, key));
	g_assert(!map_contains(dw->values, key));
	g_assert(!filled || (!filled->len == !filled->data));

	saved_key = wcopy(key, dbmw_keylen(dw, key));

	/*
	 * If we have less keys cached than our maximum, add it.
	 * Otherwise evict the least recently used key, at the head.
	 */

	if (hash_list_length(dw->keys) < dw->max_cached) {
		if (filled)
			entry = filled;
		else
			WALLOC0(entry);
	} else {
		gpointer head;

		g_assert(hash_list_length(dw->keys) == dw->max_cached);

		head = hash_list_head(dw->keys);
		entry = remove_entry(dw, head, filled != NULL, TRUE);

		g_assert(filled != NULL || entry != NULL);

		if (filled)
			entry = filled;
	}

	/*
	 * Add entry into cache.
	 */

	g_assert(entry);

	hash_list_append(dw->keys, saved_key);
	map_insert(dw->values, saved_key, entry);

	return entry;
}
Пример #7
0
/**
 * Attach a UDP TX scheduling layer to a TX stack.
 *
 * @param us			the UDP TX scheduler to use
 * @param tx			the TX driver attaching to the scheduler
 * @param writable		TX handler to invoke when we can write new data
 */
void
udp_sched_attach(udp_sched_t *us, const txdrv_t *tx,
	inputevt_handler_t writable)
{
	struct udp_tx_stack key, *uts;

	udp_sched_check(us);

	key.tx = tx;
	g_assert(!hash_list_contains(us->stacks, &key));

	WALLOC(uts);
	uts->tx = tx;
	uts->writable = writable;

	hash_list_append(us->stacks, uts);
}
Пример #8
0
static bool
udp_ping_register(const struct guid *muid,
	host_addr_t addr, uint16 port,
	udp_ping_cb_t cb, void *data, bool multiple)
{
	struct udp_ping *ping;
	uint length;

	g_assert(muid);
	g_return_val_if_fail(udp_pings, FALSE);

	if (hash_list_contains(udp_pings, muid)) {
		/* Probably a duplicate */
		return FALSE;
	}

	/* random early drop */
	length = hash_list_length(udp_pings);
	if (length >= UDP_PING_MAX) {
		return FALSE;
	} else if (length > (UDP_PING_MAX / 4) * 3) {
		if (random_value(UDP_PING_MAX - 1) < length)
			return FALSE;
	}

	WALLOC(ping);
	ping->muid = *muid;
	ping->added = tm_time();
	{
		gnet_host_t host;
		gnet_host_set(&host, addr, port);
		ping->host = atom_host_get(&host);
	}

	if (cb != NULL) {
		WALLOC0(ping->callback);
		ping->callback->cb = cb;
		ping->callback->data = data;
		ping->callback->multiple = booleanize(multiple);
	} else {
		ping->callback = NULL;
	}
	hash_list_append(udp_pings, ping);
	return TRUE;
}
Пример #9
0
static void
uhc_list_append(const char *host)
{
	struct uhc *uhc;

	g_return_if_fail(host);

	uhc = uhc_new(host);
	if (hash_list_contains(uhc_list, uhc)) {
		g_warning("duplicate bootstrap UHC: \"%s\"", uhc->host);
		uhc_free(&uhc);
		return;
	}

	if (GNET_PROPERTY(bootstrap_debug) > 1)
		g_debug("adding UHC %s", host);

	hash_list_append(uhc_list, uhc);
}
Пример #10
0
/**
 * Insert a key/value pair in the table.
 *
 * If the key already exists, the value is replaced (but the old key is kept).
 *
 * For ordering purposes, the key is appended to the list of keys, unless it
 * already existed in which case its position is unchanged.
 */
void
ohash_table_insert(ohash_table_t *oh, const void *key, const void *value)
{
	struct ohash_pair pk;

	ohash_table_check(oh);

	pk.oh = oh;
	pk.key = key;

	if (!hash_list_contains(oh->hl, &pk)) {
		struct ohash_pair *op;

		WALLOC(op);
		op->key = key;
		op->value = value;
		op->oh = oh;
		hash_list_append(oh->hl, op);
	}
}
Пример #11
0
/**
 * Insert an attribute to the table.
 *
 * If the attribute already existed, the value is replaced so that the
 * older position is kept.  Otherwsie, the new attribute is appended to the
 * list of existing attributes.
 *
 * @return TRUE when we create a new attribute, FALSE if we replaced the
 * value of an existing one.
 */
static bool
xattr_table_insert(xattr_table_t *xat, struct xattr *xa)
{
	struct xattr *old;

	xattr_table_check(xat);
	g_assert(xa != NULL);

	old = xattr_table_lookup_key(xat, xa);

	if (old != NULL) {
		HFREE_NULL(old->value);
		old->value = xa->value;
		xa->value = NULL;
		xattr_free(xa);
		return FALSE;
	} else {
		hash_list_append(xat->hl, xa);
		return TRUE;
	}
}
Пример #12
0
/**
 * Upon reception of an UDP pong, check whether we had a matching registered
 * ping bearing the given MUID.
 *
 * If there was a callback atttached to the reception of a reply, invoke it
 * before returning UDP_PONG_HANDLED.
 *
 * The ``host'' paramaeter MUST be a stack or static pointer to a gnet_host_t,
 * and NOT the address of a dynamically allocated host because gnet_host_copy()
 * is going to be used on it.
 *
 * @param n		the gnutella node replying
 * @param host	if non-NULL, filled with the host to whom we sent the ping
 *
 * @return TRUE if indeed this was a reply for a ping we sent.
 */
enum udp_pong_status
udp_ping_is_registered(const struct gnutella_node *n, gnet_host_t *host)
{
	const struct guid *muid = gnutella_header_get_muid(&n->header);

	if (udp_pings) {
		struct udp_ping *ping;

		ping = hash_list_remove(udp_pings, muid);
		if (ping != NULL) {
			if (host != NULL) {
				/*
				 * Let caller know the exact IP:port of the host we contacted,
				 * since the replying party can use a different port (which
				 * we may not be able to contact, whereas we know the targeted
				 * port did cause a reply).
				 */
				gnet_host_copy(host, ping->host);
			}

			if (ping->callback) {
				(*ping->callback->cb)(UDP_PING_REPLY, n, ping->callback->data);
				if (ping->callback->multiple) {
					ping->callback->got_reply = TRUE;
					ping->added = tm_time();	/* Delay expiration */
					hash_list_append(udp_pings, ping);
				} else {
					udp_ping_free(ping);
				}
				return UDP_PONG_HANDLED;
			}
			udp_ping_free(ping);
			return UDP_PONG_SOLICITED;
		}
	}
	return UDP_PONG_UNSOLICITED;
}