1 /* cache .c - a LRU cache
2  *
3  * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de>
4  *
5  * This file is part of pysqlite.
6  *
7  * This software is provided 'as-is', without any express or implied
8  * warranty.  In no event will the authors be held liable for any damages
9  * arising from the use of this software.
10  *
11  * Permission is granted to anyone to use this software for any purpose,
12  * including commercial applications, and to alter it and redistribute it
13  * freely, subject to the following restrictions:
14  *
15  * 1. The origin of this software must not be misrepresented; you must not
16  *    claim that you wrote the original software. If you use this software
17  *    in a product, an acknowledgment in the product documentation would be
18  *    appreciated but is not required.
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  *    misrepresented as being the original software.
21  * 3. This notice may not be removed or altered from any source distribution.
22  */
23 
24 #include "cache.h"
25 #include <limits.h>
26 
27 /* only used internally */
pysqlite_new_node(PyObject * key,PyObject * data)28 pysqlite_Node* pysqlite_new_node(PyObject* key, PyObject* data)
29 {
30     pysqlite_Node* node;
31 
32     node = (pysqlite_Node*) (pysqlite_NodeType.tp_alloc(&pysqlite_NodeType, 0));
33     if (!node) {
34         return NULL;
35     }
36 
37     Py_INCREF(key);
38     node->key = key;
39 
40     Py_INCREF(data);
41     node->data = data;
42 
43     node->prev = NULL;
44     node->next = NULL;
45 
46     return node;
47 }
48 
pysqlite_node_dealloc(pysqlite_Node * self)49 void pysqlite_node_dealloc(pysqlite_Node* self)
50 {
51     Py_DECREF(self->key);
52     Py_DECREF(self->data);
53 
54     Py_TYPE(self)->tp_free((PyObject*)self);
55 }
56 
pysqlite_cache_init(pysqlite_Cache * self,PyObject * args,PyObject * kwargs)57 int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs)
58 {
59     PyObject* factory;
60     int size = 10;
61 
62     self->factory = NULL;
63 
64     if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) {
65         return -1;
66     }
67 
68     /* minimum cache size is 5 entries */
69     if (size < 5) {
70         size = 5;
71     }
72     self->size = size;
73     self->first = NULL;
74     self->last = NULL;
75 
76     self->mapping = PyDict_New();
77     if (!self->mapping) {
78         return -1;
79     }
80 
81     Py_INCREF(factory);
82     self->factory = factory;
83 
84     self->decref_factory = 1;
85 
86     return 0;
87 }
88 
pysqlite_cache_dealloc(pysqlite_Cache * self)89 void pysqlite_cache_dealloc(pysqlite_Cache* self)
90 {
91     pysqlite_Node* node;
92     pysqlite_Node* delete_node;
93 
94     if (!self->factory) {
95         /* constructor failed, just get out of here */
96         return;
97     }
98 
99     /* iterate over all nodes and deallocate them */
100     node = self->first;
101     while (node) {
102         delete_node = node;
103         node = node->next;
104         Py_DECREF(delete_node);
105     }
106 
107     if (self->decref_factory) {
108         Py_DECREF(self->factory);
109     }
110     Py_DECREF(self->mapping);
111 
112     Py_TYPE(self)->tp_free((PyObject*)self);
113 }
114 
pysqlite_cache_get(pysqlite_Cache * self,PyObject * args)115 PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args)
116 {
117     PyObject* key = args;
118     pysqlite_Node* node;
119     pysqlite_Node* ptr;
120     PyObject* data;
121 
122     node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
123     if (node) {
124         /* an entry for this key already exists in the cache */
125 
126         /* increase usage counter of the node found */
127         if (node->count < LONG_MAX) {
128             node->count++;
129         }
130 
131         /* if necessary, reorder entries in the cache by swapping positions */
132         if (node->prev && node->count > node->prev->count) {
133             ptr = node->prev;
134 
135             while (ptr->prev && node->count > ptr->prev->count) {
136                 ptr = ptr->prev;
137             }
138 
139             if (node->next) {
140                 node->next->prev = node->prev;
141             } else {
142                 self->last = node->prev;
143             }
144             if (node->prev) {
145                 node->prev->next = node->next;
146             }
147             if (ptr->prev) {
148                 ptr->prev->next = node;
149             } else {
150                 self->first = node;
151             }
152 
153             node->next = ptr;
154             node->prev = ptr->prev;
155             if (!node->prev) {
156                 self->first = node;
157             }
158             ptr->prev = node;
159         }
160     }
161     else if (PyErr_Occurred()) {
162         return NULL;
163     }
164     else {
165         /* There is no entry for this key in the cache, yet. We'll insert a new
166          * entry in the cache, and make space if necessary by throwing the
167          * least used item out of the cache. */
168 
169         if (PyDict_GET_SIZE(self->mapping) == self->size) {
170             if (self->last) {
171                 node = self->last;
172 
173                 if (PyDict_DelItem(self->mapping, self->last->key) != 0) {
174                     return NULL;
175                 }
176 
177                 if (node->prev) {
178                     node->prev->next = NULL;
179                 }
180                 self->last = node->prev;
181                 node->prev = NULL;
182 
183                 Py_DECREF(node);
184             }
185         }
186 
187         data = PyObject_CallFunction(self->factory, "O", key);
188 
189         if (!data) {
190             return NULL;
191         }
192 
193         node = pysqlite_new_node(key, data);
194         if (!node) {
195             return NULL;
196         }
197         node->prev = self->last;
198 
199         Py_DECREF(data);
200 
201         if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) {
202             Py_DECREF(node);
203             return NULL;
204         }
205 
206         if (self->last) {
207             self->last->next = node;
208         } else {
209             self->first = node;
210         }
211         self->last = node;
212     }
213 
214     Py_INCREF(node->data);
215     return node->data;
216 }
217 
pysqlite_cache_display(pysqlite_Cache * self,PyObject * args)218 PyObject* pysqlite_cache_display(pysqlite_Cache* self, PyObject* args)
219 {
220     pysqlite_Node* ptr;
221     PyObject* prevkey;
222     PyObject* nextkey;
223     PyObject* display_str;
224 
225     ptr = self->first;
226 
227     while (ptr) {
228         if (ptr->prev) {
229             prevkey = ptr->prev->key;
230         } else {
231             prevkey = Py_None;
232         }
233 
234         if (ptr->next) {
235             nextkey = ptr->next->key;
236         } else {
237             nextkey = Py_None;
238         }
239 
240         display_str = PyUnicode_FromFormat("%S <- %S -> %S\n",
241                                            prevkey, ptr->key, nextkey);
242         if (!display_str) {
243             return NULL;
244         }
245         PyObject_Print(display_str, stdout, Py_PRINT_RAW);
246         Py_DECREF(display_str);
247 
248         ptr = ptr->next;
249     }
250 
251     Py_RETURN_NONE;
252 }
253 
254 static PyMethodDef cache_methods[] = {
255     {"get", (PyCFunction)pysqlite_cache_get, METH_O,
256         PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")},
257     {"display", (PyCFunction)pysqlite_cache_display, METH_NOARGS,
258         PyDoc_STR("For debugging only.")},
259     {NULL, NULL}
260 };
261 
262 PyTypeObject pysqlite_NodeType = {
263         PyVarObject_HEAD_INIT(NULL, 0)
264         MODULE_NAME "Node",                             /* tp_name */
265         sizeof(pysqlite_Node),                          /* tp_basicsize */
266         0,                                              /* tp_itemsize */
267         (destructor)pysqlite_node_dealloc,              /* tp_dealloc */
268         0,                                              /* tp_vectorcall_offset */
269         0,                                              /* tp_getattr */
270         0,                                              /* tp_setattr */
271         0,                                              /* tp_as_async */
272         0,                                              /* tp_repr */
273         0,                                              /* tp_as_number */
274         0,                                              /* tp_as_sequence */
275         0,                                              /* tp_as_mapping */
276         0,                                              /* tp_hash */
277         0,                                              /* tp_call */
278         0,                                              /* tp_str */
279         0,                                              /* tp_getattro */
280         0,                                              /* tp_setattro */
281         0,                                              /* tp_as_buffer */
282         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
283         0,                                              /* tp_doc */
284         0,                                              /* tp_traverse */
285         0,                                              /* tp_clear */
286         0,                                              /* tp_richcompare */
287         0,                                              /* tp_weaklistoffset */
288         0,                                              /* tp_iter */
289         0,                                              /* tp_iternext */
290         0,                                              /* tp_methods */
291         0,                                              /* tp_members */
292         0,                                              /* tp_getset */
293         0,                                              /* tp_base */
294         0,                                              /* tp_dict */
295         0,                                              /* tp_descr_get */
296         0,                                              /* tp_descr_set */
297         0,                                              /* tp_dictoffset */
298         (initproc)0,                                    /* tp_init */
299         0,                                              /* tp_alloc */
300         0,                                              /* tp_new */
301         0                                               /* tp_free */
302 };
303 
304 PyTypeObject pysqlite_CacheType = {
305         PyVarObject_HEAD_INIT(NULL, 0)
306         MODULE_NAME ".Cache",                           /* tp_name */
307         sizeof(pysqlite_Cache),                         /* tp_basicsize */
308         0,                                              /* tp_itemsize */
309         (destructor)pysqlite_cache_dealloc,             /* tp_dealloc */
310         0,                                              /* tp_vectorcall_offset */
311         0,                                              /* tp_getattr */
312         0,                                              /* tp_setattr */
313         0,                                              /* tp_as_async */
314         0,                                              /* tp_repr */
315         0,                                              /* tp_as_number */
316         0,                                              /* tp_as_sequence */
317         0,                                              /* tp_as_mapping */
318         0,                                              /* tp_hash */
319         0,                                              /* tp_call */
320         0,                                              /* tp_str */
321         0,                                              /* tp_getattro */
322         0,                                              /* tp_setattro */
323         0,                                              /* tp_as_buffer */
324         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
325         0,                                              /* tp_doc */
326         0,                                              /* tp_traverse */
327         0,                                              /* tp_clear */
328         0,                                              /* tp_richcompare */
329         0,                                              /* tp_weaklistoffset */
330         0,                                              /* tp_iter */
331         0,                                              /* tp_iternext */
332         cache_methods,                                  /* tp_methods */
333         0,                                              /* tp_members */
334         0,                                              /* tp_getset */
335         0,                                              /* tp_base */
336         0,                                              /* tp_dict */
337         0,                                              /* tp_descr_get */
338         0,                                              /* tp_descr_set */
339         0,                                              /* tp_dictoffset */
340         (initproc)pysqlite_cache_init,                  /* tp_init */
341         0,                                              /* tp_alloc */
342         0,                                              /* tp_new */
343         0                                               /* tp_free */
344 };
345 
pysqlite_cache_setup_types(void)346 extern int pysqlite_cache_setup_types(void)
347 {
348     int rc;
349 
350     pysqlite_NodeType.tp_new = PyType_GenericNew;
351     pysqlite_CacheType.tp_new = PyType_GenericNew;
352 
353     rc = PyType_Ready(&pysqlite_NodeType);
354     if (rc < 0) {
355         return rc;
356     }
357 
358     rc = PyType_Ready(&pysqlite_CacheType);
359     return rc;
360 }
361