void dmp_range_splice(
	dmp_pool *pool, dmp_range *onto, dmp_pos pos, dmp_range *from)
{
	dmp_node *tail;

	dmp_range_normalize(pool, from);

	tail = dmp_node_at(pool, from->end);

	if (pos == -1) {
		dmp_node *after = dmp_node_at(pool, onto->end);
		tail->next  = after->next;
		after->next = from->start;
		onto->end   = from->end;
	}
	else if (pos == 0) {
		tail->next  = onto->start;
		onto->start = from->start;
	}
	else {
		dmp_node *after = dmp_node_at(pool, pos);
		tail->next  = after->next;
		after->next = from->start;
	}
}
dmp_pos dmp_range_insert(
	dmp_pool *pool, dmp_range *run, dmp_pos pos,
	int op, const char *data, uint32_t offset, uint32_t len)
{
	dmp_node *node;
	dmp_pos added_at = alloc_node(pool, op, data, offset, len);
	if (added_at < 0)
		return pos;

	node = dmp_node_at(pool, added_at);

	if (pos == -1) {
		dmp_node *end = dmp_node_at(pool, run->end);
		node->next = end->next;
		end->next  = added_at;
		run->end   = added_at;
	}
	else if (pos == 0) {
		node->next = run->start;
		run->start = added_at;
	}
	else {
		dmp_node *after = dmp_node_at(pool, pos);
		node->next  = after->next;
		after->next = added_at;
	}

	return added_at;
}
void test_ranges_0(void)
{
	dmp_pool pool, *p = &pool;
	dmp_range range, *r = &range;
	uint32_t used;

	assert(dmp_pool_alloc(p, 4) == 0);

	assert(dmp_range_init(p, r, 0, "", 0, 0) > 0);
	assert(r->start > 0);
	assert(r->start == r->end);
	assert(dmp_range_len(p, r) == 1);
	assert(dmp_range_insert(p, r, -1, 0, "ab", 0, 2) > 0);
	assert(r->start != r->end);
	assert(dmp_range_len(p, r) == 2);
	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(dmp_range_len(p, r) == 3);
	assert(dmp_range_insert(p, r, -1, 0, "cd", 0, 2) > 0);
	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(dmp_range_insert(p, r, -1, 0, "ef", 0, 2) > 0);
	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(r->start != r->end);
	assert(dmp_range_len(p, r) == 7);
	progress();

	used = p->pool_used;
	dmp_range_normalize(p, r);
	assert(dmp_range_len(p, r) == 3);
	assert(strcmp(dmp_node_at(p, r->start)->text, "ab") == 0);
	assert(strcmp(dmp_node_at(p, r->end)->text, "ef") == 0);

	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(p->pool_used == used);

	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(p->pool_used == used);

	assert(dmp_range_insert(p, r, -1, 0, "", 0, 0) > 0);
	assert(p->pool_used == used + 1);
	progress();
}
static dmp_pos alloc_node(
	dmp_pool *pool, int op, const char *data, uint32_t offset, uint32_t len)
{
	dmp_pos   pos;
	dmp_node *node;

	assert(pool && data && op >= -1 && op <= 1);

	/* don't insert zero length INSERT or DELETE ops */
	if (len == 0 && op != 0)
		return -1;

	if (pool->free_list > 0) {
		pos = pool->free_list;
		node = dmp_node_at(pool, pos);
		pool->free_list = node->next;
	}
	else {
		if (pool->pool_used >= pool->pool_size)
			(void)grow_pool(pool);

		pos = pool->pool_used;
		pool->pool_used += 1;
		node = dmp_node_at(pool, pos);
	}

	node->text = data + offset;
	node->len  = len;
	node->op   = op;
	node->next = -1;

#ifdef BUGALICIOUS
	if (len > 0)
		fprintf(stderr, "adding <%c'%.*s'> (len %d) %02x\n",
				!node->op ? '=' : node->op < 0 ? '-' : '+',
				node->len, node->text, node->len, (int)*node->text);
#endif

	return pos;
}
int dmp_range_len(dmp_pool *pool, dmp_range *run)
{
	int count = 0;
	dmp_pos scan;

	for (scan = run->start; scan != -1; ) {
		dmp_node *node = dmp_node_at(pool, scan);
		count++;
		scan = node->next;
	}

	return count;
}
void dmp_range_normalize(dmp_pool *pool, dmp_range *range)
{
	dmp_pos last_nonzero = -1, *pos = &range->start;

	while (*pos != -1) {
		dmp_node *node = dmp_node_at(pool, *pos);
		if (!node->len) {
			*pos = node->next;
			dmp_node_release(pool, dmp_node_pos(pool, node));
		} else {
			last_nonzero = *pos;
			pos = &node->next;
		}
	}

	if (last_nonzero >= 0)
		range->end = last_nonzero;
}
void dmp_node_release(dmp_pool *pool, dmp_pos idx)
{
	dmp_node *node = dmp_node_at(pool, idx);
	node->next = pool->free_list;
	pool->free_list = idx;
}