/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

#include	<fcntl.h>
#include	"memory_debug.h"
#include	"task.h"
#include	"utils.h"
#include	"change_endian.h"
#include	"rcache.h"
#include	"filespace.h"
#include	"favt.h"
#include	"resource.h"
#include	"routing.h"
#include	"rt_cache.h"

#define RT_MAX_LIST_LEN	(30000/sizeof(PN_RT_CACHE_ELEMENT))

typedef struct acc_pn_rt_cache_header {
	ACC_HEADER		h;
	PN_RT_CACHE_HEADER	d;
} ACC_PN_RT_CACHE_HEADER;

typedef struct acc_pn_rt_cache_list {
	ACC_HEADER		h;
	PN_RT_CACHE_LIST	d;
} ACC_PN_RT_CACHE_LIST;

typedef struct acc_pn_rt_cache_data {
	ACC_HEADER		h;
	PN_RT_CACHE_DATA	d;
} ACC_PN_RT_CACHE_DATA;


typedef struct rt_cache_list {
	struct rt_cache_list *	next;
	PN_RT_CACHE_ELEMENT	el;
} RT_CACHE_LIST;

typedef struct rtc_timer_index {
	unsigned int		fofs;
	unsigned int		timer;
} RTC_TIMER_INDEX;

static RCACHE_BLK routing_cache;
static ACC_PN_RT_CACHE_HEADER * rt_cache_header;
static PDB * rc_p;
static int max_rt_cache_counter = 100000;
int rt_rcache_flags;

/*
void
ring_check(char * str)
{
	local_lock_rcache(&routing_cache);
	check_favt_ring(str);
	unlock_rcache(&routing_cache);
}
*/

void
change_endian_rt_cache_header(PN_RT_CACHE_HEADER * d)
{
	change_endian_header(&d->h);
	change_endian_i(d->flags);
	change_endian_i(d->count);
	change_endian_i(d->url_tree);
	change_endian_i(d->timer_tree);
}

void
change_endian_rt_cache_element(PN_RT_CACHE_ELEMENT * d)
{
	change_endian_i(d->data);
	change_endian_i(d->update);
	change_endian_i(d->flags);
}

void
change_endian_rt_cache_list_to_host(PN_RT_CACHE_LIST * d)
{
int i;
int len;
PN_RT_CACHE_ELEMENT * ptr;
	change_endian_header(&d->h);
	change_endian_i(d->next);
	len = d->h.size - sizeof(*d);
	if ( len < 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	ptr = (PN_RT_CACHE_ELEMENT *)(d+1);
	for ( i = 0 ; i < len/sizeof(PN_RT_CACHE_ELEMENT) ; i ++ , ptr ++ ) {
		change_endian_rt_cache_element(ptr);
	}
}

void
change_endian_rt_cache_list_to_net(PN_RT_CACHE_LIST * d)
{
int i;
int len;
PN_RT_CACHE_ELEMENT * ptr;
	len = d->h.size - sizeof(*d);
	ptr = (PN_RT_CACHE_ELEMENT *)(d+1);
	for ( i = 0 ; i < len/sizeof(PN_RT_CACHE_ELEMENT) ; i ++ , ptr ++ ) {
		change_endian_rt_cache_element(ptr);
	}
	change_endian_header(&d->h);
	change_endian_i(d->next);
}

void
change_endian_rt_cache_data_to_host(PN_RT_CACHE_DATA * d)
{
int len;
int i;
L_CHAR * ptr;
	change_endian_header(&d->h);
	change_endian_i(d->sts);
	change_endian_i(d->modify);
	change_endian_i(d->update);
	len = d->h.size - sizeof(*d);
	if ( len < 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	ptr = (L_CHAR*)(d+1);
	for ( i = 0 ; i < len/sizeof(L_CHAR) ; i ++ , ptr ++ ){
		change_endian_i(*ptr);
	}
}

void
change_endian_rt_cache_data_to_net(PN_RT_CACHE_DATA * d)
{
int len;
int i;
L_CHAR * ptr;
	len = d->h.size - sizeof(*d);
	ptr = (L_CHAR*)(d+1);
	for ( i = 0 ; i < len/sizeof(L_CHAR) ; i ++ , ptr ++ ){
		change_endian_i(*ptr);
	}

	change_endian_header(&d->h);
	change_endian_i(d->sts);
	change_endian_i(d->modify);
	change_endian_i(d->update);
}

void
rtc_timer_endian(RTC_TIMER_INDEX * ix)
{
	change_endian_i(ix->fofs);
	change_endian_i(ix->timer);
}

int
cmp_rtc_timer(RTC_TIMER_INDEX * ix1,RTC_TIMER_INDEX * ix2)
{
	if ( ix1->timer < ix2->timer )
		return -1;
	if ( ix1->timer > ix2->timer )
		return 1;
	if ( ix1->fofs < ix2->fofs )
		return -1;
	if ( ix1->fofs > ix2->fofs )
		return 1;
	return 0;
}

void
_purge_rt_cache_header()
{
ACC_PN_RT_CACHE_HEADER * h;
	if ( rc_p == 0 )
		return;
	if ( rt_cache_header == 0 )
		return;
	h = d_alloc(sizeof(ACC_PN_RT_CACHE_HEADER));
	*h = *rt_cache_header;
	change_endian_rt_cache_header(&h->d);
	write_filespace(rc_p,h);
	d_f_ree(h);
}

int
_new_rt_cache()
{
PDB * p;
L_CHAR * encoding;
ACC_PN_RT_CACHE_HEADER * h;
FAVT_ROOT * rt;
int ret;
	h = 0;
	encoding = l_string(std_cm,"UCS4");
	p = open_filespace(
		n_string(std_cm,routing_cache.path),
		O_CREAT|O_RDWR|O_TRUNC,0644,
		PF_USEFREELIST,FT_RT_CACHE,encoding,0,0);
	if ( p == 0 )
		return -1;
	ret = -1;
	h = d_alloc(sizeof(*h));
	h->h.fofs = 0;
	h->d.h.size = sizeof(h->d);
	h->d.h.type = PNT_RT_CACHE_HEADER;
	h->h.fofs = alloc_filespace(p,&h->d.h);
	strcpy(h->d.version,RT_CACHE_VERSION);
	h->d.flags = 0;
	h->d.count = 0;
	rt = favt_alloc_root(p,FAT_RTC_URL,favt_string_endian);
	if ( rt == 0 ) {
		ret = -1;
		goto end;
	}
	h->d.url_tree = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_RTC_TIMER,rtc_timer_endian);
	if ( rt == 0 ) {
		ret = -1;
		goto end;
	}
	h->d.timer_tree = rt->h.fofs;

	change_endian_rt_cache_header(&h->d);
	write_filespace(p,h);
	ret = 0;

	close_filespace(p);
end:
	if ( h )
		d_f_ree(h);
	return ret;
}


void
init_rt_cache()
{
}



void
_open_rt_cache()
{
PN_RT_CACHE_HEADER * h;
unsigned int fofs;

retry:
	rc_p = open_filespace(
		n_string(std_cm,routing_cache.path),O_RDWR,
		0600,PF_USEFREELIST,FT_RT_CACHE,0,0,0);
	if ( rc_p ) {
		fofs = 0;
		h = get_file_record(&fofs,
			rc_p,PNT_RT_CACHE_HEADER);
		if ( h == 0 ) {
			close_filespace(rc_p);
			goto remake;
		}
		rt_cache_header = d_alloc(sizeof(*rt_cache_header));
		rt_cache_header->d = *h;
		rt_cache_header->h.fofs = fofs;
		d_f_ree(h);
		change_endian_rt_cache_header(&rt_cache_header->d);
		if ( (rt_cache_header->d.flags & RTC_F_WRITE) ||
				strcmp(rt_cache_header->d.version,
					RT_CACHE_VERSION) ) {
			close_filespace(rc_p);
			d_f_ree(rt_cache_header);
			rt_cache_header = 0;
			goto remake;
		}
	}
	else {
		goto remake;
	}
	routing_cache.err = RE_RUN;
	return;
remake:
	routing_cache.err = RE_DESTROY;
	return;
}


void
_close_rt_cache()
{
	if ( rc_p == 0 )
		return;
	close_filespace(rc_p);
	rc_p = 0;
	d_f_ree(rt_cache_header);
	rt_cache_header = 0;
}

void
remake_rt_cache()
{
	local_lock_rcache(&routing_cache);
	if ( routing_cache.err != RE_DESTROY )
		goto end;
	wlock_rcache(&routing_cache);
	if ( routing_cache.err != RE_DESTROY )
		goto end;
	if ( rc_p )
		_close_rt_cache();
	if ( _new_rt_cache() < 0 ) {
		routing_cache.err = RE_STOP;
	}
	else {
		_open_rt_cache();
		if ( routing_cache.err == RE_DESTROY )
			routing_cache.err = RE_STOP;
	}
	routing_cache.err = RE_RUN;
end:
	unlock_rcache(&routing_cache);
}


unsigned int
get_rt_url(FAVT_NODE ** np,FAVT_ROOT * r,L_CHAR * url)
{
FAVT_NODE * n;
STRING_INDEX * ix;
int len;

	len = l_strlen(url);

	ix = d_alloc(STRING_IX_LENGTH_1(len));
	ix->fofs = 0;
	l_strcpy(ix->d,url);
	n = favt_search(r,root_node(r),ix,favt_cmp_string_1);
	d_f_ree(ix);
	if ( n == 0 )
		return 0;
	ix = n->data;
	if ( np )
		*np = n;
	return ix->fofs;
}


MP_ROUTE *
get_hit_data(unsigned int fofs)
{
ACC_PN_RT_CACHE_DATA * d;
MP_ROUTE * ret;
MP_PATH * p;
L_CHAR * ptr;
int len,_len;
	d = read_filespace(rc_p,fofs);
	if ( d == 0 ) {
		routing_cache.err = RE_DESTROY;
		return 0;
	}
	change_endian_rt_cache_data_to_host(&d->d);
	if ( routing_cache.err == RE_DESTROY )
		return 0;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->sts = RS_COPY;
	ret->update = d->d.update;
	ret->modify = d->d.modify;
	ptr = (L_CHAR*)((&d->d)+1);
	len = d->d.h.size - sizeof(d->d);
	for ( ; len > 0 ; ) {
		p = d_alloc(sizeof(*p));
		p->url = ll_copy_str(ptr);
		p->next = 0;
		if ( ret->head == 0 ) {
			ret->head = ret->tail = p;
		}
		else {
			ret->tail->next = p;
			ret->tail = p;
		}
		_len = l_strlen(ptr);
		ptr += _len+1;
		len -= (_len+1) * sizeof(L_CHAR);
	}
	return ret;
}

RT_CACHE_LIST **
get_rt_cache_list_add(RT_CACHE_LIST ** pos,unsigned int * fofs)
{
ACC_PN_RT_CACHE_LIST * lst;
int i,len;
PN_RT_CACHE_ELEMENT * ptr;
RT_CACHE_LIST * n;

	lst = read_filespace(rc_p,*fofs);
	if ( lst == 0 ) {
		routing_cache.err = RE_DESTROY;
		return 0;
	}
	change_endian_rt_cache_list_to_host(&lst->d);
	if ( routing_cache.err == RE_DESTROY )
		return 0;
	len = lst->d.h.size - sizeof(lst->d);
	ptr = (PN_RT_CACHE_ELEMENT*)((&lst->d)+1);
	for ( i = 0 ; i < len/sizeof(PN_RT_CACHE_ELEMENT) ; i ++ , ptr ++ ) {
		n = d_alloc(sizeof(*n));
		n->el = *ptr;
		n->next = 0;
		*pos = n;
		pos = &n->next;
	}
	*fofs = lst->d.next;
	d_f_ree(lst);
	return pos;
}

RT_CACHE_LIST *
get_rt_cache_list(unsigned int fofs)
{
RT_CACHE_LIST * ret, ** retp;
	ret = 0;
	retp = &ret;
	for ( ; fofs ; ) {
		retp = get_rt_cache_list_add(retp,&fofs);
		if ( retp == 0 )
			break;
		if ( fofs == 0 )
			break;
	}
	return ret;
}

void
free_rt_cache_list(RT_CACHE_LIST * lst)
{
RT_CACHE_LIST * p;
	for ( ; lst ; ) {
		p = lst;
		lst = lst->next;
		d_f_ree(p);
	}
}

int
length_of_rt_cache_list(RT_CACHE_LIST * lst)
{
int ret;
	for ( ret = 0 ; lst ; lst = lst->next , ret ++ );
	return ret;
}

unsigned int
save_rt_cache_list(RT_CACHE_LIST * lst)
{
int len;
int size;
int d_len;
int i,j;
ACC_PN_RT_CACHE_LIST ** d;
PN_RT_CACHE_ELEMENT * ep;
unsigned int ret;

	len = length_of_rt_cache_list(lst);
	d_len = len / RT_MAX_LIST_LEN;
	if ( len % RT_MAX_LIST_LEN )
		d_len ++;
	d = d_alloc(sizeof(*d) * d_len);
	for ( i = 0 ; i < d_len ; i ++ ) {
		if ( len > RT_MAX_LIST_LEN )
			size = RT_MAX_LIST_LEN;
		else	size = len;
		d[i] = d_alloc(PN_RT_CACHE_LIST_S(size) + sizeof(ACC_HEADER));
		d[i]->h.fofs = 0;
		d[i]->d.next = 0;
		ep = (PN_RT_CACHE_ELEMENT*)((&d[i]->d)+1);
		for ( j = 0 ; j < size ; j ++ , ep ++ ) {
			*ep = lst->el;
			lst = lst->next;
			len --;
		}
		d[i]->d.h.size = sizeof(d[i]->d) + 
			size * sizeof(PN_RT_CACHE_ELEMENT);
		d[i]->d.h.type = PNT_RT_CACHE_LIST;
		d[i]->h.fofs = alloc_filespace(rc_p,&d[i]->d.h);

	}
	for ( i = 0 ; i < d_len ; i ++ ) {
		if ( i == d_len - 1 )
			d[i]->d.next = 0;
		else	d[i]->d.next = d[i+1]->h.fofs;
		change_endian_rt_cache_list_to_net(&d[i]->d);
		write_filespace(rc_p,d[i]);
	}
	ret = d[0]->h.fofs;
	for ( i = 0 ; i < d_len ; i ++ )
		d_f_ree(d[i]);
	d_f_ree(d);
	return ret;
}

void
delete_rt_cache_list(unsigned int fofs)
{
ACC_PN_RT_CACHE_LIST * h;
	for ( ; fofs ; ) {
		h = read_filespace(rc_p,fofs);
		if ( h == 0 ) {
			routing_cache.err = RE_DESTROY;
			return;
		}
		change_endian_rt_cache_list_to_host(&h->d);
		if ( routing_cache.err == RE_DESTROY ) {
			d_f_ree(h);
			return;
		}
		if ( h->d.h.type != PNT_RT_CACHE_LIST ) {
			routing_cache.err = RE_DESTROY;
			d_f_ree(h);
			return;
		}
		fofs = h->d.next;
		free_filespace(rc_p,h->h.fofs);
		d_f_ree(h);
	}
}


MP_ROUTE *
get_rt_cache(L_CHAR * start,L_CHAR * target)
{
MP_ROUTE * ret;
FAVT_ROOT * r;
unsigned int start_ix,target_ix;
RT_CACHE_LIST * lst1,* lst2, * p1, * p2;

retry:
	ret = 0;
	local_lock_rcache(&routing_cache);
	if ( (rt_rcache_flags & RCF_ROUTING_READ) == 0 )
		goto idle;
	if ( routing_cache.err == 0 )
		goto idle;
	if ( routing_cache.err < 0 ) {
		unlock_rcache(&routing_cache);
		remake_rt_cache();
		if ( routing_cache.err <= 0 )
			goto idle2;
		goto retry;
	}
	rlock_rcache(&routing_cache);
	lst1 = lst2 = 0;
	if ( rc_p == 0 ) {
		_open_rt_cache();
		if ( routing_cache.err < 0 )
			goto end;
	}
	r = get_root(rc_p,rt_cache_header->d.url_tree,favt_string_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		ret = 0;
		goto end;
	}
	start_ix = get_rt_url(0,r,start);
	if ( start_ix == 0 ) {
		ret = 0;
		goto end;
	}
	target_ix = get_rt_url(0,r,target);
	if ( target_ix == 0 ) {
		ret = 0;
		goto end;
	}
	lst1 = get_rt_cache_list(start_ix);
	lst2 = get_rt_cache_list(target_ix);
	p1 = lst1;
	p2 = lst2;
	for ( ; p1 && p2 ; ) {
		if ( p1->el.data == p2->el.data ) {
			ret = get_hit_data(p1->el.data);
			goto end;
		}
		if ( p1->el.update > p2->el.update )
			goto inc_p1;
		if ( p1->el.update < p2->el.update )
			goto inc_p2;
		if ( p1->el.data > p2->el.data )
			goto inc_p1;
	inc_p2:
		p2 = p2->next;
		continue;
	inc_p1:
		p1 = p1->next;
		continue;
	}
end:
	free_rt_cache_list(lst1);
	free_rt_cache_list(lst2);
idle:
	unlock_rcache(&routing_cache);
	remake_rt_cache();
idle2:
	return ret;
}


unsigned int
search_strict_route(L_CHAR * start,L_CHAR * target)
{
FAVT_ROOT * r;
unsigned int start_ix,target_ix,ret;
RT_CACHE_LIST * lst1,* lst2, * p1, * p2;


	lst1 = lst2 = 0;
	r = get_root(rc_p,rt_cache_header->d.url_tree,favt_string_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		ret = 0;
		goto end;
	}
	start_ix = get_rt_url(0,r,start);
	if ( start_ix == 0 ) {
		ret = 0;
		goto end;
	}
	target_ix = get_rt_url(0,r,target);
	if ( target_ix == 0 ) {
		ret = 0;
		goto end;
	}
	lst1 = get_rt_cache_list(start_ix);
	lst2 = get_rt_cache_list(target_ix);
	p1 = lst1;
	p2 = lst2;
	ret = 0;
	for ( ; p1 && p2 ; ) {
		if ( p1->el.data == p2->el.data &&
			(p1->el.flags & RTC_LF_TERMINAL) == 0 &&
			(p2->el.flags & RTC_LF_TERMINAL) == 0 ) {

			ret = p1->el.data;
			goto end;
		}
		if ( p1->el.update > p2->el.update )
			goto inc_p1;
		if ( p1->el.update < p2->el.update )
			goto inc_p2;
		if ( p1->el.data > p2->el.data )
			goto inc_p1;
	inc_p2:
		p2 = p2->next;
		continue;
	inc_p1:
		p1 = p1->next;
		continue;
	}
end:
	free_rt_cache_list(lst1);
	free_rt_cache_list(lst2);
	return ret;

}

void
delete_rt_url_tree(L_CHAR * url,unsigned int data)
{
FAVT_ROOT * r;
unsigned int ixd,new_ixd;
RT_CACHE_LIST * lst1, ** p1, * target;
int ret;
FAVT_NODE * n;
STRING_INDEX * ix;
	lst1 = 0;
	r = get_root(rc_p,rt_cache_header->d.url_tree,favt_string_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	ixd = get_rt_url(&n,r,url);
	if ( ixd == 0 )
		return;
	lst1 = get_rt_cache_list(ixd);
	p1 = &lst1;
	for ( ; *p1 ; ) {
		target = *p1;
		if ( target->el.data != data ) {
			p1 = &target->next;
			continue;
		}
		*p1 = target->next;
		d_f_ree(target);
	}
	delete_rt_cache_list(ixd);
	if ( lst1 ) {
		new_ixd = save_rt_cache_list(lst1);
		ix = n->data;
		ix->fofs = new_ixd;
		n->h.flags |= FAF_DIRTY;
	}
	else {
		ix = n->data;
		n = favt_delete(r,&r->node,ix,favt_cmp_string_1);
		favt_free_node(n);
	}
}


void
delete_rt_cache_data(unsigned int p_data)
{
ACC_PN_RT_CACHE_DATA * d;
L_CHAR * ptr;
int len;
int p_pos,p_len;
FAVT_ROOT * r;
FAVT_NODE * n;
RTC_TIMER_INDEX ix;
	d = read_filespace(rc_p,p_data);
	if ( d == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	change_endian_rt_cache_data_to_host(&d->d);
	if ( routing_cache.err == RE_DESTROY )
		return;
	if ( d->d.h.type != PNT_RT_CACHE_DATA ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	p_pos = 0;
	len = (d->d.h.size - sizeof(&d->d))/sizeof(L_CHAR);
	ptr = (L_CHAR*)((&d->d)+1);
	p_pos = 0;
	for ( ; p_pos < len ; ) {
		delete_rt_url_tree(ptr,p_data);
		p_len = l_strlen(ptr)+1;
		ptr += p_len;
		p_pos += p_len;
	}
	r = get_root(rc_p,rt_cache_header->d.timer_tree,rtc_timer_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	n = favt_delete(r,&r->node,&ix,cmp_rtc_timer);
	favt_free_node(n);
	rt_cache_header->d.count --;
	_purge_rt_cache_header();
}

void
_purge_rt_cache(L_CHAR * start,L_CHAR * target)
{
unsigned int p_data;
	p_data = search_strict_route(start,target);
	if ( p_data == 0 )
		return;
	delete_rt_cache_data(p_data);
}

unsigned int
save_rt_cache_data(MP_ROUTE * mp)
{
ACC_PN_RT_CACHE_DATA * d;
int size;
MP_PATH * p;
L_CHAR * ptr;
int len;
int ret;
	size = sizeof(d->d);
	for ( p = mp->head ; p ; p = p->next )
		size += (l_strlen(p->url)+1)*sizeof(L_CHAR);
	d = d_alloc(size+sizeof(ACC_HEADER));
	d->d.h.type = PNT_RT_CACHE_DATA;
	d->d.h.size = size;
	d->d.update = mp->update;
	d->d.modify = mp->modify;
	d->d.sts = mp->sts;
	ptr = (L_CHAR*)((&d->d)+1);
	for ( p = mp->head ; p ; p = p->next ) {
		len = l_strlen(p->url)+1;
		memcpy(ptr,p->url,len*sizeof(L_CHAR));
		ptr += len;
	}
	d->h.fofs = alloc_filespace(rc_p,&d->d.h);
	change_endian_rt_cache_data_to_net(&d->d);
	write_filespace(rc_p,d);
	ret = d->h.fofs;
	d_f_ree(d);
	return ret;
}

void
insert_rt_cache_list(L_CHAR * url,PN_RT_CACHE_ELEMENT * el)
{
FAVT_ROOT * r;
unsigned int ixd,new_ixd;
RT_CACHE_LIST * lst1, ** p1, * target, * ins;
int ret,len;
FAVT_NODE * n, * nn;
STRING_INDEX * ix;
	lst1 = 0;

	r = get_root(rc_p,rt_cache_header->d.url_tree,favt_string_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	ixd = get_rt_url(&n,r,url);
	if ( ixd == 0 ) {
		len = l_strlen(url);
		ix = d_alloc(STRING_IX_LENGTH_1(len));
		ix->fofs = 0;
		l_strcpy(ix->d,url);
		n = favt_alloc_node(r,ix,STRING_IX_LENGTH_1(len));
		if ( n == 0 ) {
			routing_cache.err = RE_DESTROY;
			d_f_ree(ix);
			return;
		}
		nn = favt_insert(r,&r->node,n,favt_cmp_string_1);
		if ( nn != n )
			er_panic("insert_rt_cache_list");
		d_f_ree(ix);
		lst1 = 0;
	}
	else {
		lst1 = get_rt_cache_list(ixd);
	}
	ins = d_alloc(sizeof(*ins));
	ins->el = *el;
	p1 = &lst1;
	for ( ; *p1 ; p1 = &(*p1)->next ) {
		target = *p1;
		if ( target->el.update < ins->el.update )
			break;
		if ( target->el.update > ins->el.update )
			continue;
		if ( target->el.data < ins->el.data )
			break;
		if ( target->el.data > ins->el.data )
			continue;
		er_panic("???");
	}
	ins->next = *p1;
	*p1 = ins;

	if ( ixd )
		delete_rt_cache_list(ixd);
	new_ixd = save_rt_cache_list(lst1);
	ix = n->data;
	ix->fofs = new_ixd;
	n->h.flags |= FAF_DIRTY;
	
}


void
insert_rt_timer(unsigned int update,unsigned int p_data)
{
FAVT_ROOT * r;
FAVT_NODE * n, * nn;
RTC_TIMER_INDEX ix;
	r = get_root(rc_p,rt_cache_header->d.timer_tree,rtc_timer_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	ix.fofs = p_data;
	ix.timer = update;
	n = favt_alloc_node(r,&ix,sizeof(ix));
	if ( n == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	nn = favt_insert(r,&r->node,n,cmp_rtc_timer);
	if ( nn != n )
		er_panic("insert_rt_cache_list");
	rt_cache_header->d.count ++;
	_purge_rt_cache_header();
}


int
purge_func(FAVT_NODE * a,FAVT_NODE ** n)
{
	*n = a;
	return 1;
}

void
purge_rt_cache_data()
{
FAVT_NODE * n;
FAVT_ROOT * r;
RTC_TIMER_INDEX * ix;
unsigned int p_data;
	r = get_root(rc_p,rt_cache_header->d.timer_tree,rtc_timer_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		return;
	}
	n = 0;
	favt_trace_from_small(r,root_node(r),purge_func,&n);
	if ( n == 0 )
		return;
	ix = n->data;
	p_data = ix->fofs;
	delete_rt_cache_data(p_data);
}

int
set_rt_cache(MP_ROUTE * mp)
{
unsigned int p_data;
int ret;
MP_PATH * p;
PN_RT_CACHE_ELEMENT el;
int cnt;


retry:
	ret = -1;
	local_lock_rcache(&routing_cache);
	if ( routing_cache.err == 0 )
		goto idle;

	mp->update = get_xltime();

	if ( routing_cache.err <= 0 ) {
		unlock_rcache(&routing_cache);
		remake_rt_cache();
		if ( routing_cache.err <= 0 )
			goto idle2;
		goto retry;
	}
	wlock_rcache(&routing_cache);
	if ( rc_p == 0 ) {
		_open_rt_cache();
		if ( routing_cache.err < 0 )
			goto end;
	}
	_purge_rt_cache(mp->head->url,mp->tail->url);

	p_data = save_rt_cache_data(mp);
	if ( p_data == 0 ) {
		routing_cache.err = RE_DESTROY;
		ret = -1;
		goto end;
	}
	for ( p = mp->head ; p ; p = p->next ) {
		el.data = p_data;
		el.update = mp->update;
		if ( p == mp->head || p == mp->tail )
			el.flags = RTC_LF_TERMINAL;
		else	el.flags = 0;
		insert_rt_cache_list(p->url,&el);
	}
	insert_rt_timer(mp->update,p_data);

	cnt = 0;
	for ( ; rt_cache_header->d.count > max_rt_cache_counter &&
			cnt < 10 ; cnt ++ )
		purge_rt_cache_data();
	ret = 0;
end:	{}
idle:
	unlock_rcache(&routing_cache);
	remake_rt_cache();
idle2:
	return ret;
}



void
purge_rt_cache(L_CHAR * url)
{
RT_CACHE_LIST * lst, * p;
FAVT_ROOT * r;
unsigned int ix;

retry:
	local_lock_rcache(&routing_cache);
	if ( routing_cache.err == 0 )
		goto idle;
	if ( routing_cache.err <= 0 ) {
		unlock_rcache(&routing_cache);
		remake_rt_cache();
		if ( routing_cache.err <= 0 )
			goto idle2;
		goto retry;
	}
	wlock_rcache(&routing_cache);
	if ( rc_p == 0 ) {
		_open_rt_cache();
		if ( routing_cache.err < 0 )
			goto end;
	}
	r = get_root(rc_p,rt_cache_header->d.url_tree,favt_string_endian);
	if ( r == 0 ) {
		routing_cache.err = RE_DESTROY;
		goto end;
	}
	ix = get_rt_url(0,r,url);
	if ( ix == 0 )
		goto end;
	lst = get_rt_cache_list(ix);
	for ( p = lst ; p ; p = p->next )
		delete_rt_cache_data(p->el.data);
	free_rt_cache_list(lst);

end:	{}
idle:
	unlock_rcache(&routing_cache);
	remake_rt_cache();
idle2:
	return;
}


void
flush_rt_cache()
{
	local_lock_rcache(&routing_cache);
	_close_rt_cache();
	unlock_rcache(&routing_cache);
}


void
_disable_rt_cache()
{
	_close_rt_cache();
	routing_cache.err = RE_IDLE;
}


void
disable_rt_cache()
{
	local_lock_rcache(&routing_cache);
	_disable_rt_cache();
	rt_rcache_flags = 0;
	unlock_rcache(&routing_cache);
}



void
enable_rt_cache(int flags)
{
	local_lock_rcache(&routing_cache);
	if ( flags & (RCF_ROUTING|RCF_ROUTING_READ) )
		new_rcache_blk(&routing_cache,"routing.rch");
	else 	_disable_rt_cache();
	rt_rcache_flags = flags;
	unlock_rcache(&routing_cache);
}
