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