/** * 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); } }
/** * 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; }
/** * 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); }
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); }
/** * 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; }
/** * 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; }
/** * 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); }
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; }
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); }
/** * 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); } }
/** * 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; } }
/** * 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; }