/src/netcdf-c/libdispatch/ncxcache.c
Line | Count | Source |
1 | | /* |
2 | | Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata |
3 | | See LICENSE.txt for license information. |
4 | | */ |
5 | | |
6 | | /** \file \internal |
7 | | Internal netcdf-4 functions. |
8 | | |
9 | | This file contains functions for manipulating NCxcache objects. |
10 | | |
11 | | Warning: This code depends critically on the assumption that |
12 | | |void*| == |uintptr_t| |
13 | | |
14 | | */ |
15 | | |
16 | | /* 0 => no debug */ |
17 | | #define DEBUG 0 |
18 | | #define CATCH |
19 | | |
20 | | /* Define this for debug so that table sizes are small */ |
21 | | #define SMALLTABLE |
22 | | |
23 | | #ifdef CATCH |
24 | | /* Warning: do not evalue x more than once */ |
25 | 0 | #define THROW(x) throw(x) |
26 | 0 | static void breakpoint(void) {} |
27 | | static int ignore[] = {0}; |
28 | | static int throw(int x) |
29 | 0 | { |
30 | 0 | int* p; |
31 | 0 | if(x != 0) { |
32 | 0 | for(p=ignore;*p;p++) {if(x == *p) break;} |
33 | 0 | if(*p == 0) breakpoint(); |
34 | 0 | } |
35 | 0 | return x; |
36 | 0 | } |
37 | | #else |
38 | | #define THROW(x) (x) |
39 | | #endif |
40 | | |
41 | | #include "config.h" |
42 | | #include <stdlib.h> |
43 | | #include <stdio.h> |
44 | | #include <string.h> |
45 | | #ifdef HAVE_STDINT_H |
46 | | #include <stdint.h> |
47 | | #endif |
48 | | #include <assert.h> |
49 | | |
50 | | #include "nc4internal.h" |
51 | | #include "ncexhash.h" |
52 | | #include "ncxcache.h" |
53 | | |
54 | | #ifdef SMALLTABLE |
55 | | /* Keep the table sizes small initially */ |
56 | | #define DFALTTABLESIZE 4 |
57 | 0 | #define DFALTLEAFLEN 4 |
58 | | #else |
59 | | #define DFALTTABLESIZE 32 |
60 | | #define DFALTLEAFLEN 12 |
61 | | #endif |
62 | | |
63 | | static void insertafter(NCxnode* current, NCxnode* node); |
64 | | static void unlinknode(NCxnode* node); |
65 | | |
66 | | #if DEBUG > 0 |
67 | | void verifylru(NCxcache* cache); |
68 | | |
69 | | static void |
70 | | xverify(NCxcache* cache) |
71 | | { |
72 | | verifylru(cache); |
73 | | } |
74 | | |
75 | | void |
76 | | verifylru(NCxcache* cache) |
77 | | { |
78 | | NCxnode* p; |
79 | | for(p=cache->lru.next;p != &cache->lru;p=p->next) { |
80 | | if(p->next == NULL || p->prev == NULL) { |
81 | | xverify(cache); |
82 | | } |
83 | | } |
84 | | } |
85 | | #endif |
86 | | |
87 | | /* Locate object by hashkey in an NCxcache */ |
88 | | int |
89 | | ncxcachelookup(NCxcache* NCxcache, ncexhashkey_t hkey, void** op) |
90 | 0 | { |
91 | 0 | int stat = NC_NOERR; |
92 | 0 | uintptr_t inode = 0; |
93 | 0 | NCxnode* node = NULL; |
94 | |
|
95 | 0 | if(NCxcache == NULL) return THROW(NC_EINVAL); |
96 | 0 | assert(NCxcache->map != NULL); |
97 | 0 | if((stat=ncexhashget(NCxcache->map,hkey,&inode))) |
98 | 0 | {stat = THROW(NC_ENOOBJECT); goto done;} /* not present */ |
99 | 0 | node = (void*)inode; |
100 | 0 | if(op) *op = node->content; |
101 | |
|
102 | 0 | done: |
103 | 0 | return stat; |
104 | 0 | } |
105 | | |
106 | | /* Move object to the front of the LRU list */ |
107 | | int |
108 | | ncxcachetouch(NCxcache* cache, ncexhashkey_t hkey) |
109 | 0 | { |
110 | 0 | int stat = NC_NOERR; |
111 | 0 | uintptr_t inode = 0; |
112 | 0 | NCxnode* node = NULL; |
113 | |
|
114 | 0 | if(cache == NULL) return THROW(NC_EINVAL); |
115 | 0 | if((stat=ncexhashget(cache->map,hkey,&inode))) |
116 | 0 | {stat = THROW(NC_ENOOBJECT); goto done;} /* not present */ |
117 | 0 | node = (void*)inode; |
118 | | /* unlink */ |
119 | 0 | unlinknode(node); |
120 | | /* Relink into front of chain */ |
121 | 0 | insertafter(&cache->lru,node); |
122 | | #if DEBUG > 0 |
123 | | verifylru(cache); |
124 | | #endif |
125 | |
|
126 | 0 | done: |
127 | 0 | return stat; |
128 | 0 | } |
129 | | |
130 | | /* Add object to the cache */ |
131 | | int |
132 | | ncxcacheinsert(NCxcache* cache, const ncexhashkey_t hkey, void* o) |
133 | 0 | { |
134 | 0 | int stat = NC_NOERR; |
135 | 0 | uintptr_t inode = 0; |
136 | 0 | NCxnode* node = NULL; |
137 | |
|
138 | 0 | if(cache == NULL) return THROW(NC_EINVAL); |
139 | | |
140 | | #ifndef NCXUSER |
141 | | node = calloc(1,sizeof(NCxnode)); |
142 | | #else |
143 | 0 | node = (NCxnode*)o; |
144 | 0 | #endif |
145 | 0 | node->content = o; /* Cheat and make content point to the node part*/ |
146 | 0 | inode = (uintptr_t)node; |
147 | 0 | stat = ncexhashput(cache->map,hkey,inode); |
148 | 0 | if(stat) |
149 | 0 | goto done; |
150 | | /* link into the LRU chain at front */ |
151 | 0 | insertafter(&cache->lru,node); |
152 | | #if DEBUG > 0 |
153 | | verifylru(cache); |
154 | | #endif |
155 | 0 | node = NULL; |
156 | 0 | done: |
157 | | #ifndef NCXUSER |
158 | | if(node) nullfree(node); |
159 | | #endif |
160 | 0 | return THROW(stat); |
161 | 0 | } |
162 | | |
163 | | /* Remove object from the index;*/ |
164 | | int |
165 | | ncxcacheremove(NCxcache* cache, ncexhashkey_t hkey, void** op) |
166 | 0 | { |
167 | 0 | int stat = NC_NOERR; |
168 | 0 | uintptr_t inode = 0; |
169 | 0 | NCxnode* node = NULL; |
170 | |
|
171 | 0 | if(cache == NULL) return THROW(NC_EINVAL); |
172 | | |
173 | | /* Remove from the hash map */ |
174 | 0 | if((stat=ncexhashremove(cache->map,hkey,&inode))) |
175 | 0 | {stat = NC_ENOOBJECT; goto done;} /* not present */ |
176 | 0 | node = (NCxnode*)inode; |
177 | | /* unlink */ |
178 | 0 | unlinknode(node); |
179 | | #if DEBUG > 0 |
180 | | verifylru(cache); |
181 | | #endif |
182 | 0 | if(op) { |
183 | 0 | *op = node->content; |
184 | 0 | } |
185 | | #ifndef NCXUSER |
186 | | nullfree(node); |
187 | | #endif |
188 | |
|
189 | 0 | done: |
190 | 0 | return THROW(stat); |
191 | 0 | } |
192 | | |
193 | | /* Free a cache */ |
194 | | void |
195 | | ncxcachefree(NCxcache* cache) |
196 | 0 | { |
197 | 0 | NCxnode* lru = NULL; |
198 | |
|
199 | 0 | if(cache == NULL) return; |
200 | 0 | lru = &cache->lru; |
201 | |
|
202 | | #ifndef NCXUSER |
203 | | { |
204 | | NCxnode* p = NULL; |
205 | | NCxnode* next = NULL; |
206 | | /* walk the lru chain */ |
207 | | next = NULL; |
208 | | for(p=lru->next;p != lru;) { |
209 | | next = p->next; |
210 | | nullfree(p); |
211 | | p = next; |
212 | | } |
213 | | } |
214 | | #endif |
215 | |
|
216 | 0 | lru->next = (lru->prev = lru); /*reset*/ |
217 | 0 | ncexhashmapfree(cache->map); |
218 | 0 | free(cache); |
219 | 0 | } |
220 | | |
221 | | /* Create a new cache holding at least size objects */ |
222 | | int |
223 | | ncxcachenew(size_t leaflen, NCxcache** cachep) |
224 | 0 | { |
225 | 0 | int stat = NC_NOERR; |
226 | 0 | NCxcache* cache = NULL; |
227 | |
|
228 | 0 | if(leaflen == 0) leaflen = DFALTLEAFLEN; |
229 | |
|
230 | 0 | cache = calloc(1,sizeof(NCxcache)); |
231 | 0 | if(cache == NULL) |
232 | 0 | {stat = NC_ENOMEM; goto done;} |
233 | 0 | cache->map = ncexhashnew((int)leaflen); |
234 | 0 | if(cache->map == NULL) |
235 | 0 | {stat = NC_ENOMEM; goto done;} |
236 | 0 | cache->lru.next = &cache->lru; |
237 | 0 | cache->lru.prev = &cache->lru; |
238 | 0 | if(cachep) {*cachep = cache; cache = NULL;} |
239 | |
|
240 | 0 | done: |
241 | 0 | ncxcachefree(cache); |
242 | 0 | return THROW(stat); |
243 | 0 | } |
244 | | |
245 | | void |
246 | | ncxcacheprint(NCxcache* cache) |
247 | 0 | { |
248 | 0 | int i; |
249 | 0 | NCxnode* p = NULL; |
250 | |
|
251 | 0 | fprintf(stderr,"NCxcache: lru="); |
252 | 0 | fprintf(stderr,"{"); |
253 | 0 | for(i=0,p=cache->lru.next;p != &cache->lru;p=p->next,i++) { |
254 | 0 | if(i>0) fprintf(stderr,","); |
255 | 0 | fprintf(stderr,"%p:%p",p,p->content); |
256 | 0 | } |
257 | 0 | fprintf(stderr,"}\n"); |
258 | 0 | ncexhashprint(cache->map); |
259 | 0 | } |
260 | | |
261 | | void* |
262 | | ncxcachefirst(NCxcache* cache) |
263 | 0 | { |
264 | 0 | if(cache == NULL) return NULL; |
265 | 0 | if(ncexhashcount(cache->map) == 0) |
266 | 0 | return NULL; |
267 | 0 | return cache->lru.next->content; |
268 | 0 | } |
269 | | |
270 | | void* |
271 | | ncxcachelast(NCxcache* cache) |
272 | 0 | { |
273 | 0 | if(cache == NULL) return NULL; |
274 | 0 | if(ncexhashcount(cache->map) == 0) |
275 | 0 | return NULL; |
276 | 0 | return cache->lru.prev->content; |
277 | 0 | } |
278 | | |
279 | | /* Insert node after current */ |
280 | | static void |
281 | | insertafter(NCxnode* current, NCxnode* node) |
282 | 0 | { |
283 | 0 | NCxnode* curnext = current->next; |
284 | 0 | current->next = node; |
285 | 0 | node->prev = current; |
286 | 0 | node->next = curnext; |
287 | 0 | curnext->prev = node; |
288 | 0 | } |
289 | | |
290 | | /* Remove node from chain */ |
291 | | static void |
292 | | unlinknode(NCxnode* node) |
293 | 0 | { |
294 | 0 | NCxnode* next; |
295 | 0 | NCxnode* prev; |
296 | 0 | assert(node != NULL); |
297 | 0 | next = node->next; |
298 | 0 | prev = node->prev; |
299 | | /* repair the chain */ |
300 | 0 | next->prev = prev; |
301 | 0 | prev->next = next; |
302 | 0 | node->next = node->prev = NULL; |
303 | 0 | } |
304 | | |
305 | | |
306 | | ncexhashkey_t |
307 | | ncxcachekey(const void* key, size_t size) |
308 | 0 | { |
309 | 0 | return ncexhashkey((unsigned char*)key,size); |
310 | 0 | } |
311 | | |