Annotation of src/usr.bin/cvs/cache.c, Revision 1.2
1.2 ! tedu 1: /* $OpenBSD: cache.c,v 1.1.1.1 2004/07/13 22:02:40 jfb Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.2 ! tedu 4: * All rights reserved.
1.1 jfb 5: *
1.2 ! tedu 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
1.1 jfb 9: *
1.2 ! tedu 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.2 ! tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.2 ! tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
27: #include <sys/param.h>
28: #include <sys/queue.h>
29: #include <sys/time.h>
30:
31: #include <stdlib.h>
32: #include <stdio.h>
33: #include <unistd.h>
34: #include <errno.h>
35: #include <string.h>
36:
37: #include "log.h"
38: #include "rcs.h"
39: #include "cache.h"
40:
41: #define RCS_CACHE_BUCKETS 256
42:
43:
44: struct rcs_cachent {
45: u_int rc_hits;
46: u_int8_t rc_hash;
47: RCSFILE *rc_rfp;
48: struct timeval rc_atime;
49:
50: TAILQ_ENTRY(rcs_cachent) rc_list;
51: TAILQ_ENTRY(rcs_cachent) rc_lru;
52: };
53:
54:
55: static u_int8_t rcs_cache_hash (const char *);
56:
57:
58: TAILQ_HEAD(rcs_cachebkt, rcs_cachent) rcs_cache[RCS_CACHE_BUCKETS];
59:
60: TAILQ_HEAD(rcs_lruhead, rcs_cachent) rcs_cache_lru;
61:
62: u_int rcs_cache_maxent;
63: u_int rcs_cache_nbent;
64:
65:
66:
67: /*
68: * rcs_cache_init()
69: *
70: * Initialize the RCS file data cache.
71: * Returns 0 on success, -1 on failure.
72: */
73: int
74: rcs_cache_init(u_int maxent)
75: {
76: u_int i;
77:
78: for (i = 0; i < RCS_CACHE_BUCKETS; i++)
79: TAILQ_INIT(&rcs_cache[i]);
80:
81: TAILQ_INIT(&rcs_cache_lru);
82:
83: rcs_cache_maxent = maxent;
84: rcs_cache_nbent = 0;
85:
86: return (0);
87: }
88:
89:
90: /*
91: * rcs_cache_destroy()
92: */
93: void
94: rcs_cache_destroy(void)
95: {
96: }
97:
98:
99: /*
100: * rcs_cache_fetch()
101: *
102: */
103: RCSFILE*
104: rcs_cache_fetch(const char *path)
105: {
106: u_int8_t hash;
107: struct rcs_cachent *rcp;
108: RCSFILE *rfp;
109:
110: rfp = NULL;
111: hash = rcs_cache_hash(path);
112:
113: TAILQ_FOREACH(rcp, &(rcs_cache[hash]), rc_list) {
114: if (strcmp(path, rcp->rc_rfp->rf_path) == 0) {
115: rfp = rcp->rc_rfp;
116: break;
117: }
118: }
119:
120: if (rcp != NULL) {
121: (void)gettimeofday(&(rcp->rc_atime), NULL);
122: rcp->rc_hits++;
123:
124: /* move this item back at the end of the LRU */
125: TAILQ_REMOVE(&rcs_cache_lru, rcp, rc_lru);
126: TAILQ_INSERT_TAIL(&rcs_cache_lru, rcp, rc_lru);
127:
128: /* increment reference count for caller */
129: rfp->rf_ref++;
130: }
131:
132: return (rfp);
133: }
134:
135:
136: /*
137: * rcs_cache_store()
138: *
139: * Store the RCSFILE <rfp> in the RCS file cache. By storing the file, its
140: * reference count gets incremented for the cache reference, so the caller
141: * should still rcs_close() the file once they are done with it.
142: * Returns 0 on success, or -1 on failure.
143: */
144: int
145: rcs_cache_store(RCSFILE *rfp)
146: {
147: struct rcs_cachent *rcp, *old_rcp;
148:
149: /* don't store the same element twice */
150: if (rcs_cache_fetch(rfp->rf_path) != NULL)
151: return (0);
152:
153: rcp = (struct rcs_cachent *)malloc(sizeof(*rcp));
154: if (rcp == NULL) {
155: cvs_log(LP_ERRNO, "failed to allocate RCS cache entry");
156: return (-1);
157: }
158:
159: rcp->rc_hits = 0;
160:
161: rcp->rc_rfp = rfp;
162: rfp->rf_ref++;
163:
164: rcp->rc_hash = rcs_cache_hash(rfp->rf_path);
165:
166: rcs_cache_nbent++;
167: if (rcs_cache_nbent == rcs_cache_maxent) {
168: /* ditch the oldest entry in the LRU */
169: old_rcp = TAILQ_FIRST(&rcs_cache_lru);
170: TAILQ_REMOVE(&rcs_cache_lru, old_rcp, rc_lru);
171: TAILQ_REMOVE(&(rcs_cache[old_rcp->rc_hash]), old_rcp, rc_list);
172:
173: /* free our reference */
174: rcs_close(old_rcp->rc_rfp);
175: free(old_rcp);
176: }
177:
178: TAILQ_INSERT_TAIL(&(rcs_cache[rcp->rc_hash]), rcp, rc_list);
179: TAILQ_INSERT_TAIL(&rcs_cache_lru, rcp, rc_lru);
180:
181: return (0);
182: }
183:
184:
185: /*
186: * rcs_cache_hash()
187: *
188: * Hash the <path> string.
189: */
190: static u_int8_t
191: rcs_cache_hash(const char *path)
192: {
193: const char *sp;
194: u_int8_t hash;
195:
196: hash = 0;
197: for (sp = path; *sp != '\0'; sp++)
198: hash ^= (*sp << 3) ^ (*sp >> 2);
199:
200: return (hash);
201: }