| #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include <ndb.h> | 
 |  | 
 | struct Ndbcache | 
 | { | 
 | 	Ndbcache	*next; | 
 | 	char		*attr; | 
 | 	char		*val; | 
 | 	Ndbs		s; | 
 | 	Ndbtuple	*t;	 | 
 | }; | 
 |  | 
 | enum | 
 | { | 
 | 	Maxcached=	128 | 
 | }; | 
 |  | 
 | static void | 
 | ndbcachefree(Ndbcache *c) | 
 | { | 
 | 	free(c->val); | 
 | 	free(c->attr); | 
 | 	if(c->t) | 
 | 		ndbfree(c->t); | 
 | 	free(c); | 
 | } | 
 |  | 
 | static Ndbtuple* | 
 | ndbcopy(Ndb *db, Ndbtuple *from_t, Ndbs *from_s, Ndbs *to_s) | 
 | { | 
 | 	Ndbtuple *first, *to_t, *last, *line; | 
 | 	int newline; | 
 |  | 
 | 	*to_s = *from_s; | 
 | 	to_s->t = nil; | 
 | 	to_s->db = db; | 
 |  | 
 | 	newline = 1; | 
 | 	last = nil; | 
 | 	first = nil; | 
 | 	line = nil; | 
 | 	for(; from_t != nil; from_t = from_t->entry){ | 
 | 		to_t = ndbnew(from_t->attr, from_t->val); | 
 |  | 
 | 		/* have s point to matching tuple */ | 
 | 		if(from_s->t == from_t) | 
 | 			to_s->t = to_t; | 
 |  | 
 | 		if(newline) | 
 | 			line = to_t; | 
 | 		else | 
 | 			last->line = to_t; | 
 |  | 
 | 		if(last != nil) | 
 | 			last->entry = to_t; | 
 | 		else { | 
 | 			first = to_t; | 
 | 			line = to_t; | 
 | 		} | 
 | 		to_t->entry = nil; | 
 | 		to_t->line = line; | 
 | 		last = to_t; | 
 | 		newline = from_t->line != from_t->entry; | 
 | 	} | 
 | 	return first; | 
 | } | 
 |  | 
 | /* | 
 |  *  if found, move to front | 
 |  */ | 
 | int | 
 | _ndbcachesearch(Ndb *db, Ndbs *s, char *attr, char *val, Ndbtuple **t) | 
 | { | 
 | 	Ndbcache *c, **l; | 
 |  | 
 | 	*t = nil; | 
 | 	c = nil; | 
 | 	for(l = &db->cache; *l != nil; l = &(*l)->next){ | 
 | 		c = *l; | 
 | 		if(strcmp(c->attr, attr) == 0 && strcmp(c->val, val) == 0) | 
 | 			break; | 
 | 	} | 
 | 	if(*l == nil) | 
 | 		return -1; | 
 |  | 
 | 	/* move to front */ | 
 | 	*l = c->next; | 
 | 	c->next = db->cache; | 
 | 	db->cache = c; | 
 |  | 
 | 	*t = ndbcopy(db, c->t, &c->s, s); | 
 | 	return 0; | 
 | } | 
 |  | 
 | Ndbtuple* | 
 | _ndbcacheadd(Ndb *db, Ndbs *s, char *attr, char *val, Ndbtuple *t) | 
 | { | 
 | 	Ndbcache *c, **l; | 
 |  | 
 | 	c = mallocz(sizeof *c, 1); | 
 | 	if(c == nil) | 
 | 		return nil; | 
 | 	c->attr = strdup(attr); | 
 | 	if(c->attr == nil) | 
 | 		goto err; | 
 | 	c->val = strdup(val); | 
 | 	if(c->val == nil) | 
 | 		goto err; | 
 | 	c->t = ndbcopy(db, t, s, &c->s); | 
 | 	if(c->t == nil && t != nil) | 
 | 		goto err; | 
 |  | 
 | 	/* add to front */ | 
 | 	c->next = db->cache; | 
 | 	db->cache = c; | 
 |  | 
 | 	/* trim list */ | 
 | 	if(db->ncache < Maxcached){ | 
 | 		db->ncache++; | 
 | 		return t; | 
 | 	} | 
 | 	for(l = &db->cache; (*l)->next; l = &(*l)->next) | 
 | 		; | 
 | 	c = *l; | 
 | 	*l = nil; | 
 | err: | 
 | 	ndbcachefree(c); | 
 | 	return t; | 
 | } | 
 |  | 
 | void | 
 | _ndbcacheflush(Ndb *db) | 
 | { | 
 | 	Ndbcache *c; | 
 |  | 
 | 	while(db->cache != nil){ | 
 | 		c = db->cache; | 
 | 		db->cache = c->next; | 
 | 		ndbcachefree(c); | 
 | 	} | 
 | 	db->ncache = 0; | 
 | } |