Exemple #1
0
static int recreate_nsec3_tree(const zone_contents_t *z, zone_contents_t *out)
{
	out->nsec3_nodes = hattrie_dup(z->nsec3_nodes, NULL);
	if (out->nsec3_nodes == NULL) {
		return KNOT_ENOMEM;
	}

	hattrie_iter_t *itt = hattrie_iter_begin(z->nsec3_nodes, false);
	if (itt == NULL) {
		return KNOT_ENOMEM;
	}
	while (!hattrie_iter_finished(itt)) {
		const zone_node_t *to_cpy = (zone_node_t *)*hattrie_iter_val(itt);
		zone_node_t *to_add = node_shallow_copy(to_cpy, NULL);
		if (to_add == NULL) {
			hattrie_iter_free(itt);
			return KNOT_ENOMEM;
		}
		int ret = zone_contents_add_nsec3_node(out, to_add);
		if (ret != KNOT_EOK) {
			hattrie_iter_free(itt);
			node_free(&to_add, NULL);
			return ret;
		}
		hattrie_iter_next(itt);
	}

	hattrie_iter_free(itt);
	hattrie_build_index(out->nsec3_nodes);

	return KNOT_EOK;
}
Exemple #2
0
static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out)
{
	out->nodes = hattrie_dup(z->nodes, NULL);
	if (out->nodes == NULL) {
		return KNOT_ENOMEM;
	}

	// Insert APEX first.
	zone_node_t *apex_cpy = node_shallow_copy(z->apex, NULL);
	if (apex_cpy == NULL) {
		return KNOT_ENOMEM;
	}

	// Normal additions need apex ... so we need to insert directly.
	int ret = zone_tree_insert(out->nodes, apex_cpy);
	if (ret != KNOT_EOK) {
		node_free(&apex_cpy, NULL);
		return ret;
	}

	out->apex = apex_cpy;

	hattrie_iter_t *itt = hattrie_iter_begin(z->nodes, true);
	if (itt == NULL) {
		return KNOT_ENOMEM;
	}
	while (!hattrie_iter_finished(itt)) {
		const zone_node_t *to_cpy = (zone_node_t *)*hattrie_iter_val(itt);
		if (to_cpy == z->apex) {
			// Inserted already.
			hattrie_iter_next(itt);
			continue;
		}
		zone_node_t *to_add = node_shallow_copy(to_cpy, NULL);
		if (to_add == NULL) {
			hattrie_iter_free(itt);
			return KNOT_ENOMEM;
		}

		int ret = zone_contents_add_node(out, to_add, true);
		if (ret != KNOT_EOK) {
			node_free(&to_add, NULL);
			hattrie_iter_free(itt);
			return ret;
		}
		hattrie_iter_next(itt);
	}

	hattrie_iter_free(itt);
	hattrie_build_index(out->nodes);

	return KNOT_EOK;
}
int main()
{
    hattrie_t* T = hattrie_create();
    const size_t n = 1000000;  // how many strings
    const size_t m_low  = 50;  // minimum length of each string
    const size_t m_high = 500; // maximum length of each string
    char x[501];

    size_t i, m;
    for (i = 0; i < n; ++i) {
        m = m_low + rand() % (m_high - m_low);
        randstr(x, m);
        *hattrie_get(T, x, m) = 1;
    }

    hattrie_iter_t* it;
    clock_t t0, t;
    const size_t repetitions = 100;
    size_t r;

    /* iterate in unsorted order */
    fprintf(stderr, "iterating out of order ... ");
    t0 = clock();
    for (r = 0; r < repetitions; ++r) {
        it = hattrie_iter_begin(T, false);
        while (!hattrie_iter_finished(it)) {
            hattrie_iter_next(it);
        }
        hattrie_iter_free(it);
    }
    t = clock();
    fprintf(stderr, "finished. (%0.2f seconds)\n", (double) (t - t0) / (double) CLOCKS_PER_SEC);


    /* iterate in sorted order */
    fprintf(stderr, "iterating in order ... ");
    t0 = clock();
    for (r = 0; r < repetitions; ++r) {
        it = hattrie_iter_begin(T, true);
        while (!hattrie_iter_finished(it)) {
            hattrie_iter_next(it);
        }
        hattrie_iter_free(it);
    }
    t = clock();
    fprintf(stderr, "finished. (%0.2f seconds)\n", (double) (t - t0) / (double) CLOCKS_PER_SEC);


    hattrie_free(T);

    return 0;
}
Exemple #4
0
int zone_tree_apply_inorder(zone_tree_t *tree,
                            zone_tree_apply_cb_t function,
                            void *data)
{
	if (function == NULL) {
		return KNOT_EINVAL;
	}

	if (zone_tree_is_empty(tree)) {
		return KNOT_EOK;
	}

	int result = KNOT_EOK;

	hattrie_iter_t *i = hattrie_iter_begin(tree, 1);
	while(!hattrie_iter_finished(i)) {
		result = function((zone_node_t **)hattrie_iter_val(i), data);
		if (result != KNOT_EOK) {
			break;
		}
		hattrie_iter_next(i);
	}
	hattrie_iter_free(i);

	return result;
}
Exemple #5
0
zone_node_t *zone_tree_get_next(zone_tree_t *tree,
                                const knot_dname_t *owner)
{
	if (tree == NULL || owner == NULL) {
		return NULL;
	}

	uint8_t lf[KNOT_DNAME_MAXLEN];
	knot_dname_lf(lf, owner, NULL);

	value_t *fval = NULL;
	zone_node_t *n = NULL;
	(void)hattrie_find_next(tree, (char*)lf + 1, *lf, &fval);
	if (fval == NULL) {
		/* Return first node. */
		hattrie_iter_t *it = hattrie_iter_begin(tree, true);
		if (it == NULL) {
			return NULL;
		}
		fval = hattrie_iter_val(it);
		hattrie_iter_free(it);
	}

	n = (zone_node_t *)*fval;
	/* Next node must be non-empty and auth. */
	if (n->rrset_count == 0 || n->flags & NODE_FLAGS_NONAUTH) {
		return zone_tree_get_next(tree, n->owner);
	} else {
		return n;
	}
}
Exemple #6
0
static int axfr_process_node_tree(knot_pkt_t *pkt, const void *item,
                                  struct xfr_proc *state)
{
	assert(item != NULL);

	struct axfr_proc *axfr = (struct axfr_proc*)state;

	if (axfr->i == NULL) {
		axfr->i = hattrie_iter_begin(item, true);
	}

	/* Put responses. */
	int ret = KNOT_EOK;
	zone_node_t *node = NULL;
	while (!hattrie_iter_finished(axfr->i)) {
		node = (zone_node_t *)*hattrie_iter_val(axfr->i);
		ret = axfr_put_rrsets(pkt, node, axfr);
		if (ret != KNOT_EOK) {
			break;
		}
		hattrie_iter_next(axfr->i);
	}

	/* Finished all nodes. */
	if (ret == KNOT_EOK) {
		hattrie_iter_free(axfr->i);
		axfr->i = NULL;
	}
	return ret;
}
Exemple #7
0
/*!
 * \brief Call a function for each piece of the chain formed by sorted nodes.
 */
int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes,
                                   chain_iterate_create_cb callback,
                                   nsec_chain_iterate_data_t *data)
{
	assert(nodes);
	assert(callback);

	bool sorted = true;
	hattrie_iter_t *it = hattrie_iter_begin(nodes, sorted);

	if (!it) {
		return KNOT_ENOMEM;
	}

	if (hattrie_iter_finished(it)) {
		hattrie_iter_free(it);
		return KNOT_EINVAL;
	}

	zone_node_t *first = (zone_node_t *)*hattrie_iter_val(it);
	zone_node_t *previous = first;
	zone_node_t *current = first;

	hattrie_iter_next(it);

	int result = KNOT_EOK;
	while (!hattrie_iter_finished(it)) {
		current = (zone_node_t *)*hattrie_iter_val(it);

		result = callback(previous, current, data);
		if (result == NSEC_NODE_SKIP) {
			// No NSEC should be created for 'current' node, skip
			;
		} else if (result == KNOT_EOK) {
			previous = current;
		} else {
			hattrie_iter_free(it);
			return result;
		}
		hattrie_iter_next(it);
	}

	hattrie_iter_free(it);

	return result == NSEC_NODE_SKIP ? callback(previous, first, data) :
	                 callback(current, first, data);
}
Exemple #8
0
static void axfr_query_cleanup(struct query_data *qdata)
{
	struct axfr_proc *axfr = (struct axfr_proc *)qdata->ext;

	hattrie_iter_free(axfr->i);
	ptrlist_free(&axfr->proc.nodes, qdata->mm);
	mm_free(qdata->mm, axfr);

	/* Allow zone changes (finished). */
	rcu_read_unlock();
}
Exemple #9
0
int zone_tree_get_less_or_equal(zone_tree_t *tree,
                                const knot_dname_t *owner,
                                zone_node_t **found,
                                zone_node_t **previous)
{
	if (owner == NULL || found == NULL || previous == NULL) {
		return KNOT_EINVAL;
	}

	if (zone_tree_is_empty(tree)) {
		return KNOT_ENONODE;
	}

	uint8_t lf[KNOT_DNAME_MAXLEN];
	knot_dname_lf(lf, owner, NULL);

	value_t *fval = NULL;
	int ret = hattrie_find_leq(tree, (char*)lf+1, *lf, &fval);
	if (fval) {
		*found = (zone_node_t *)(*fval);
	}
	int exact_match = 0;
	if (ret == 0) {
		if (fval) {
			*previous = (*found)->prev;
		}
		exact_match = 1;
	} else if (ret < 0) {
		*previous = *found;
		*found = NULL;
	} else if (ret > 0) {
		/* Previous should be the rightmost node.
		 * For regular zone it is the node left of apex, but for some
		 * cases like NSEC3, there is no such sort of thing (name wise).
		 */
		/*! \todo We could store rightmost node in zonetree probably. */
		hattrie_iter_t *i = hattrie_iter_begin(tree, 1);
		*previous = *(zone_node_t **)hattrie_iter_val(i); /* leftmost */
		*previous = (*previous)->prev; /* rightmost */
		*found = NULL;
		hattrie_iter_free(i);
	}

	/* Previous node for proof must be non-empty and authoritative. */
	if (*previous &&
	    ((*previous)->rrset_count == 0 || (*previous)->flags & NODE_FLAGS_NONAUTH)) {
		*previous = (*previous)->prev;
	}

	return exact_match;
}
Exemple #10
0
void test_hattrie_find_prev()
{
    fprintf(stderr, "finding previous for %zu keys ... \n", k);
    hattrie_build_index(T);
    
    hattrie_iter_t* i = hattrie_iter_begin(T, true);

    value_t* u;
    const char *key = NULL;
    char *dkey = NULL;
    char *fkey = NULL;
    size_t len = 0, flen = 0;
    
    while (!hattrie_iter_finished(i)) {
        u = hattrie_iter_val(i);
        key = hattrie_iter_key(i, &len);
        
        /* first key */
        if (!fkey) {
            fkey = malloc(len); memcpy(fkey, key, len);
            --fkey[len-1];
            flen = len;
        }

        /* check hattrie_find_leq functionality */
        dkey = realloc(dkey, len); memcpy(dkey, key, len);
        ++dkey[len-1];
        value_t *fp = NULL;
        int r = hattrie_find_leq(T, dkey, len, &fp);
        if (*fp != *u || r != -1) {
            fprintf(stderr, "[error] hattrie_find_leq should find %lu, "
                    "but found prev=%lu, rval=%d\n",
                    *u, *fp, r);
        }
        hattrie_iter_next(i);
    }
    hattrie_iter_free(i);
    
    /* check before first key */
    value_t *fp = NULL;
    int r = hattrie_find_leq(T, fkey, flen, &fp);
    if (r != 1 || fp != NULL) {
        fprintf(stderr, "[error] hattrie_find_leq should return 1 and NULL for "
                "string < first string, returned %d (%p)\n",
                r, (void*)fp);
    }
    free(fkey);
    free(dkey);
    fprintf(stderr, "done.\n");
}
Exemple #11
0
void test_hattrie_iteration()
{
    fprintf(stderr, "iterating through %zu keys ... \n", k);

    hattrie_iter_t* i = hattrie_iter_begin(T, false);

    size_t count = 0;
    value_t* u;
    value_t  v;

    size_t len;
    const char* key;

    while (!hattrie_iter_finished(i)) {
        ++count;

        key = hattrie_iter_key(i, &len);
        u   = hattrie_iter_val(i);

        v = str_map_get(M, key, len);

        if (*u != v) {
            if (v == 0) {
                fprintf(stderr, "[error] incorrect iteration (%lu, %lu)\n", *u, v);
            }
            else {
                fprintf(stderr, "[error] incorrect iteration tally (%lu, %lu)\n", *u, v);
            }
        }

        // this way we will see an error if the same key is iterated through
        // twice
        str_map_set(M, key, len, 0);

        hattrie_iter_next(i);
    }

    if (count != M->m) {
        fprintf(stderr, "[error] iterated through %zu element, expected %zu\n",
                count, M->m);
    }

    hattrie_iter_free(i);

    fprintf(stderr, "done.\n");
}
Exemple #12
0
hattrie_t* hattrie_dup(const hattrie_t* T, value_t (*nval)(value_t))
{
    hattrie_t *N = hattrie_create_n(T->bsize, &T->mm);

    /* assignment */
    if (!nval) nval = hattrie_setval;

    /*! \todo could be probably implemented faster */

    size_t l = 0;
    const char *k = 0;
    hattrie_iter_t *i = hattrie_iter_begin(T, false);
    while (!hattrie_iter_finished(i)) {
        k = hattrie_iter_key(i, &l);
        *hattrie_get(N, k, l) = nval(*hattrie_iter_val(i));
        hattrie_iter_next(i);
    }
    hattrie_iter_free(i);
    return N;
}
Exemple #13
0
void test_hattrie_sorted_iteration()
{
    fprintf(stderr, "iterating in order through %zu keys ... \n", k);

    hattrie_iter_t* i = hattrie_iter_begin(T, true);

    size_t count = 0;
    value_t* u;
    value_t  v;

    char* key_copy = malloc(m_high + 1);
    char* prev_key = malloc(m_high + 1);
    memset(prev_key, 0, m_high + 1);
    size_t prev_len = 0;

    const char *key = NULL;
    size_t len = 0;

    while (!hattrie_iter_finished(i)) {
        memcpy(prev_key, key_copy, len);
        prev_key[len] = '\0';
        prev_len = len;
        ++count;

        key = hattrie_iter_key(i, &len);

        /* memory for key may be changed on iter, copy it */
        strncpy(key_copy, key, len);

        if (prev_key != NULL && cmpkey(prev_key, prev_len, key, len) > 0) {
            fprintf(stderr, "[error] iteration is not correctly ordered.\n");
        }

        u = hattrie_iter_val(i);
        v = str_map_get(M, key, len);

        if (*u != v) {
            if (v == 0) {
                fprintf(stderr, "[error] incorrect iteration (%lu, %lu)\n", *u, v);
            }
            else {
                fprintf(stderr, "[error] incorrect iteration tally (%lu, %lu)\n", *u, v);
            }
        }

        // this way we will see an error if the same key is iterated through
        // twice
        str_map_set(M, key, len, 0);

        hattrie_iter_next(i);
    }

    if (count != M->m) {
        fprintf(stderr, "[error] iterated through %zu element, expected %zu\n",
                count, M->m);
    }

    hattrie_iter_free(i);
    free(prev_key);
    free(key_copy);

    fprintf(stderr, "done.\n");
}
Exemple #14
0
int main(int argc, char *argv[])
{
	plan_lazy();

	/* Random keys. */
	srand(time(NULL));
	unsigned key_count = 100000;
	char **keys = malloc(sizeof(char*) * key_count);
	for (unsigned i = 0; i < key_count; ++i) {
		keys[i] = str_key_rand(KEY_MAXLEN);
	}

	/* Sort random keys. */
	str_key_sort(keys, key_count);

	/* Create trie */
	value_t *val = NULL;
	hattrie_t *trie = hattrie_create();
	ok(trie != NULL, "hattrie: create");

	/* Insert keys */
	bool passed = true;
	size_t inserted = 0;
	for (unsigned i = 0; i < key_count; ++i) {
		val = hattrie_get(trie, keys[i], strlen(keys[i]) + 1);
		if (!val) {
			passed = false;
			break;
		}
		if (*val == NULL) {
			*val = keys[i];
			++inserted;
		}
	}
	ok(passed, "hattrie: insert");

	/* Check total insertions against trie weight. */
	is_int(hattrie_weight(trie), inserted, "hattrie: trie weight matches insertions");

	/* Build order-index. */
	hattrie_build_index(trie);

	/* Lookup all keys */
	passed = true;
	for (unsigned i = 0; i < key_count; ++i) {
		val = hattrie_tryget(trie, keys[i], strlen(keys[i]) + 1);
		if (val && (*val == keys[i] || strcmp(*val, keys[i]) == 0)) {
			continue;
		} else {
			diag("hattrie: mismatch on element '%u'", i);
			passed = false;
			break;
		}
	}
	ok(passed, "hattrie: lookup all keys");

	/* Lesser or equal lookup. */
	passed = true;
	for (unsigned i = 0; i < key_count; ++i) {
		if (!str_key_find_leq(trie, keys, i, key_count)) {
			passed = false;
			for (int off = -10; off < 10; ++off) {
				int k = (int)i + off;
				if (k < 0 || k >= key_count) {
					continue;
				}
				diag("[%u/%d]: %s%s", i, off, off == 0?">":"",keys[k]);
			}
			break;
		}
	}
	ok(passed, "hattrie: find lesser or equal for all keys");

	/* Next lookup. */
	passed = true;
	for (unsigned i = 0; i < key_count - 1 && passed; ++i) {
		value_t *val;
		hattrie_find_next(trie, keys[i], strlen(keys[i]), &val);
		passed = val && *val == (void *)keys[(i + 1)];
	}
	ok(passed, "hattrie: find next for all keys");

	/* Unsorted iteration */
	size_t iterated = 0;
	hattrie_iter_t *it = hattrie_iter_begin(trie, false);
	while (!hattrie_iter_finished(it)) {
		++iterated;
		hattrie_iter_next(it);
	}
	is_int(inserted, iterated, "hattrie: unsorted iteration");
	hattrie_iter_free(it);

	/* Sorted iteration. */
	char key_buf[KEY_MAXLEN] = {'\0'};
	iterated = 0;
	it = hattrie_iter_begin(trie, true);
	while (!hattrie_iter_finished(it)) {
		size_t cur_key_len = 0;
		const char *cur_key = hattrie_iter_key(it, &cur_key_len);
		if (iterated > 0) { /* Only if previous exists. */
			if (strcmp(key_buf, cur_key) > 0) {
				diag("'%s' <= '%s' FAIL\n", key_buf, cur_key);
				break;
			}
		}
		++iterated;
		memcpy(key_buf, cur_key, cur_key_len);
		hattrie_iter_next(it);
	}
	is_int(inserted, iterated, "hattrie: sorted iteration");
	hattrie_iter_free(it);

	/* Cleanup */
	for (unsigned i = 0; i < key_count; ++i) {
		free(keys[i]);
	}
	free(keys);
	hattrie_free(trie);
	return 0;
}
Exemple #15
0
int proc_update_privileges(int uid, int gid)
{
#ifdef HAVE_SETGROUPS
	/* Drop supplementary groups. */
	if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) {
		if (setgroups(0, NULL) < 0) {
			log_server_warning("Failed to drop supplementary groups"
			                   " for uid '%d' (%s).\n",
			                   getuid(), strerror(errno));
		}
# ifdef HAVE_INITGROUPS
		struct passwd *pw;
		if ((pw = getpwuid(uid)) == NULL) {
			log_server_warning("Failed to get passwd entry"
					   " for uid '%d' (%s).\n",
					   uid, strerror(errno));
		} else {
			if (initgroups(pw->pw_name, gid) < 0) {
				log_server_warning("Failed to set supplementary groups"
						   " for uid '%d' (%s).\n",
						   uid, strerror(errno));
			}
		}
# endif /* HAVE_INITGROUPS */
	}
#endif /* HAVE_SETGROUPS */

	/* Watch uid/gid. */
	if ((gid_t)gid != getgid()) {
		log_server_info("Changing group id to '%d'.\n", gid);
		if (setregid(gid, gid) < 0) {
			log_server_error("Failed to change gid to '%d'.\n",
			                 gid);
		}
	}
	if ((uid_t)uid != getuid()) {
		log_server_info("Changing user id to '%d'.\n", uid);
		if (setreuid(uid, uid) < 0) {
			log_server_error("Failed to change uid to '%d'.\n",
			                 uid);
		}
	}

	/* Check storage writeability. */
	int ret = KNOT_EOK;
	const bool sorted = false;
	hattrie_iter_t *z_iter = hattrie_iter_begin(conf()->zones, sorted);
	if (z_iter == NULL) {
		return KNOT_ERROR;
	}
	for (; !hattrie_iter_finished(z_iter); hattrie_iter_next(z_iter)) {
		conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter);
		char *lfile = strcdup(zone->storage, "/knot.lock");
		assert(lfile != NULL);
		FILE* fp = fopen(lfile, "w");
		if (fp == NULL) {
			log_server_warning("Storage directory '%s' is not "
			                   "writeable.\n", zone->storage);
			ret = KNOT_EACCES;
		} else {
			fclose(fp);
			unlink(lfile);
		}
		free(lfile);

		if (ret != KNOT_EOK) {
			break;
		}
	}
	hattrie_iter_free(z_iter);

	return ret;
}
Exemple #16
0
int main(int argc, char* argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage: bam-summarize reads.bam\n");
        exit(EXIT_FAILURE);
    }

    samfile_t* f = samopen(argv[1], "rb", NULL);
    if (f == NULL) {
        fprintf(stderr, "can't open bam file %s\n", argv[1]);
        exit(1);
    }

    bam1_t* b = bam_init1();


    hattrie_t* T = hattrie_create();

    char* qname = NULL;
    size_t qname_size = 0;

    size_t j, n = 0;
    uint32_t* cigar;
    uint32_t cigar_op, cigar_len;

    read_stat_t** val;

    while (samread(f, b) >= 0) {
        if (++n % 1000000 == 0) {
            fprintf(stderr, "\t%zu alignments\n", n);
        }

        bool perfect = true;
        bool spliced = false;
        bool gapped  = false;

        cigar = bam1_cigar(b);
        for (j = 0; j < b->core.n_cigar; ++j) {
            cigar_op  = cigar[j] & BAM_CIGAR_MASK;
            cigar_len = cigar[j] >> BAM_CIGAR_SHIFT;

            if (cigar_op == BAM_CREF_SKIP) {
                if (cigar_len < min_splice_length) gapped = true;
                else                               spliced = true;
            }
            else if (cigar_op != BAM_CMATCH)  perfect = false;

            if (cigar_op == BAM_CSOFT_CLIP || cigar_op == BAM_CHARD_CLIP) break;
        }

        /* Skip any clipped alignments. We don't want your kind! */
        if (cigar_op == BAM_CSOFT_CLIP || cigar_op == BAM_CHARD_CLIP) continue;

        /* Hack the read to include mate information. */
        if (b->core.flag & BAM_FPAIRED) {
            if (qname_size < b->core.l_qname + 3) {
                qname_size = b->core.l_qname + 3;
                qname = realloc(qname, qname_size);
            }
            memcpy(qname, bam1_qname(b), b->core.l_qname);

            if (b->core.flag & BAM_FREAD1) {
                qname[b->core.l_qname]     = '/';
                qname[b->core.l_qname + 1] = '2';
                qname[b->core.l_qname + 2] = '\0';
            }
            else {
                qname[b->core.l_qname]     = '/';
                qname[b->core.l_qname + 1] = '1';
                qname[b->core.l_qname + 2] = '\0';
            }

            val = (read_stat_t**) hattrie_get(T, qname, b->core.l_qname + 2);
        }
        else {
            val = (read_stat_t**) hattrie_get(T, bam1_qname(b), b->core.l_qname);
        }


        if (*val == NULL) {
            *val = malloc(sizeof(read_stat_t));
            memset(*val, 0, sizeof(read_stat_t));
        }

        (*val)->aln_count++;
        if (perfect) {
            if (spliced) (*val)->spliced_perfect_cnt++;
            else         (*val)->unspliced_perfect_cnt++;
        }

        if (spliced) (*val)->spliced_cnt++;
        if (gapped) (*val)->gapped_cnt++;
    }

    printf("alignment_count\t%zu\n", n);
    printf("read_count\t%zu\n", hattrie_size(T));


    /* print stats from the table */

    uint32_t multi_count = 0;

    uint32_t unspliced_perfect_cnt = 0;
    uint32_t spliced_perfect_cnt = 0;

    uint32_t spliced_cnt = 0;
    uint32_t gapped_cnt = 0;

    /* excluding multireads */
    uint32_t unique_unspliced_perfect_cnt = 0;
    uint32_t unique_spliced_perfect_cnt = 0;

    uint32_t unique_spliced_cnt = 0;
    uint32_t unique_gapped_cnt = 0;



    hattrie_iter_t* i;
    for (i = hattrie_iter_begin(T);
         !hattrie_iter_finished(i);
         hattrie_iter_next(i))
    {
        val = (read_stat_t**) hattrie_iter_val(i);

        if ((*val)->aln_count == 1) {
            unique_unspliced_perfect_cnt += (*val)->unspliced_perfect_cnt;
            unique_spliced_perfect_cnt   += (*val)->spliced_perfect_cnt;

            unique_spliced_cnt += (*val)->spliced_cnt;
            unique_gapped_cnt  += (*val)->gapped_cnt;
        }
        else multi_count++;

        unspliced_perfect_cnt += (*val)->unspliced_perfect_cnt;
        spliced_perfect_cnt   += (*val)->spliced_perfect_cnt;

        spliced_cnt += (*val)->spliced_cnt;
        gapped_cnt  += (*val)->gapped_cnt;
    }

    hattrie_iter_free(i);


    printf("multi_count\t%u\n", multi_count);
    printf("unspliced_perfect_cnt\t%u\n", unspliced_perfect_cnt);
    printf("spliced_perfect_cnt\t%u\n", spliced_perfect_cnt);
    printf("spliced_cnt\t%u\n", spliced_cnt);
    printf("gapped_cnt\t%u\n", gapped_cnt);

    printf("unique_unspliced_perfect_cnt\t%u\n", unique_unspliced_perfect_cnt);
    printf("unique_spliced_perfect_cnt\t%u\n", unique_spliced_perfect_cnt);
    printf("unique_spliced_cnt\t%u\n", unique_spliced_cnt);
    printf("unique_gapped_cnt\t%u\n", unique_gapped_cnt);


    /* free the table */
    for (i = hattrie_iter_begin(T);
         !hattrie_iter_finished(i);
         hattrie_iter_next(i))
    {
        free(* (read_stat_t**) hattrie_iter_val(i));
    }

    hattrie_iter_free(i);
    hattrie_free(T);
    free(qname);

    bam_destroy1(b);

    return 0;
}