blob: 0b02be4f0bec98570efe80fad3ec09cc47812c2a [file] [log] [blame]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001/* cache .c - a LRU cache
2 *
Florent Xiclunac934f322010-09-03 23:47:32 +00003 * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de>
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00004 *
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"
Thomas Wouters477c8d52006-05-27 19:21:47 +000025#include <limits.h>
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000026
27/* only used internally */
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000028pysqlite_Node* pysqlite_new_node(PyObject* key, PyObject* data)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000029{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000030 pysqlite_Node* node;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000031
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020032 node = (pysqlite_Node*) (pysqlite_NodeType->tp_alloc(pysqlite_NodeType, 0));
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033 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
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000049void pysqlite_node_dealloc(pysqlite_Node* self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000050{
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020051 PyTypeObject *tp = Py_TYPE(self);
52
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000053 Py_DECREF(self->key);
54 Py_DECREF(self->data);
55
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020056 tp->tp_free(self);
57 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000058}
59
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000060int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000061{
62 PyObject* factory;
63 int size = 10;
64
65 self->factory = NULL;
66
Thomas Wouters477c8d52006-05-27 19:21:47 +000067 if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) {
68 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000069 }
70
Thomas Wouters477c8d52006-05-27 19:21:47 +000071 /* minimum cache size is 5 entries */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000072 if (size < 5) {
73 size = 5;
74 }
75 self->size = size;
76 self->first = NULL;
77 self->last = NULL;
78
79 self->mapping = PyDict_New();
80 if (!self->mapping) {
81 return -1;
82 }
83
84 Py_INCREF(factory);
85 self->factory = factory;
86
87 self->decref_factory = 1;
88
89 return 0;
90}
91
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000092void pysqlite_cache_dealloc(pysqlite_Cache* self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000093{
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020094 PyTypeObject *tp = Py_TYPE(self);
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000095 pysqlite_Node* node;
96 pysqlite_Node* delete_node;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000097
98 if (!self->factory) {
99 /* constructor failed, just get out of here */
100 return;
101 }
102
Thomas Wouters477c8d52006-05-27 19:21:47 +0000103 /* iterate over all nodes and deallocate them */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000104 node = self->first;
105 while (node) {
106 delete_node = node;
107 node = node->next;
108 Py_DECREF(delete_node);
109 }
110
111 if (self->decref_factory) {
112 Py_DECREF(self->factory);
113 }
114 Py_DECREF(self->mapping);
115
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200116 tp->tp_free(self);
117 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000118}
119
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200120PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* key)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000121{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000122 pysqlite_Node* node;
123 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000124 PyObject* data;
125
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200126 node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000127 if (node) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000128 /* an entry for this key already exists in the cache */
129
130 /* increase usage counter of the node found */
131 if (node->count < LONG_MAX) {
132 node->count++;
133 }
134
135 /* if necessary, reorder entries in the cache by swapping positions */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000136 if (node->prev && node->count > node->prev->count) {
137 ptr = node->prev;
138
139 while (ptr->prev && node->count > ptr->prev->count) {
140 ptr = ptr->prev;
141 }
142
143 if (node->next) {
144 node->next->prev = node->prev;
145 } else {
146 self->last = node->prev;
147 }
148 if (node->prev) {
149 node->prev->next = node->next;
150 }
151 if (ptr->prev) {
152 ptr->prev->next = node;
153 } else {
154 self->first = node;
155 }
156
157 node->next = ptr;
158 node->prev = ptr->prev;
159 if (!node->prev) {
160 self->first = node;
161 }
162 ptr->prev = node;
163 }
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200164 }
165 else if (PyErr_Occurred()) {
166 return NULL;
167 }
168 else {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000169 /* There is no entry for this key in the cache, yet. We'll insert a new
170 * entry in the cache, and make space if necessary by throwing the
171 * least used item out of the cache. */
172
Serhiy Storchaka5ab81d72016-12-16 16:18:57 +0200173 if (PyDict_GET_SIZE(self->mapping) == self->size) {
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000174 if (self->last) {
175 node = self->last;
176
177 if (PyDict_DelItem(self->mapping, self->last->key) != 0) {
178 return NULL;
179 }
180
181 if (node->prev) {
182 node->prev->next = NULL;
183 }
184 self->last = node->prev;
185 node->prev = NULL;
186
187 Py_DECREF(node);
188 }
189 }
190
Petr Viktorinffd97532020-02-11 17:46:57 +0100191 /* We cannot replace this by PyObject_CallOneArg() since
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200192 * PyObject_CallFunction() has a special case when using a
193 * single tuple as argument. */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000194 data = PyObject_CallFunction(self->factory, "O", key);
195
196 if (!data) {
197 return NULL;
198 }
199
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000200 node = pysqlite_new_node(key, data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000201 if (!node) {
202 return NULL;
203 }
204 node->prev = self->last;
205
206 Py_DECREF(data);
207
208 if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) {
209 Py_DECREF(node);
210 return NULL;
211 }
212
213 if (self->last) {
214 self->last->next = node;
215 } else {
216 self->first = node;
217 }
218 self->last = node;
219 }
220
221 Py_INCREF(node->data);
222 return node->data;
223}
224
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000225PyObject* pysqlite_cache_display(pysqlite_Cache* self, PyObject* args)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000226{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000227 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000228 PyObject* prevkey;
229 PyObject* nextkey;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000230 PyObject* display_str;
231
232 ptr = self->first;
233
234 while (ptr) {
235 if (ptr->prev) {
236 prevkey = ptr->prev->key;
237 } else {
238 prevkey = Py_None;
239 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000240
241 if (ptr->next) {
242 nextkey = ptr->next->key;
243 } else {
244 nextkey = Py_None;
245 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000246
Amaury Forgeot d'Arc864741b2011-11-06 15:10:48 +0100247 display_str = PyUnicode_FromFormat("%S <- %S -> %S\n",
248 prevkey, ptr->key, nextkey);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000249 if (!display_str) {
250 return NULL;
251 }
252 PyObject_Print(display_str, stdout, Py_PRINT_RAW);
253 Py_DECREF(display_str);
254
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000255 ptr = ptr->next;
256 }
257
Berker Peksagfe21de92016-04-09 07:34:39 +0300258 Py_RETURN_NONE;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000259}
260
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200261static PyType_Slot pysqlite_NodeType_slots[] = {
262 {Py_tp_dealloc, pysqlite_node_dealloc},
263 {Py_tp_new, PyType_GenericNew},
264 {0, NULL},
265};
266
267static PyType_Spec pysqlite_NodeType_spec = {
268 .name = MODULE_NAME ".Node",
269 .basicsize = sizeof(pysqlite_Node),
Erlend Egeberg Aasland9031bd42020-10-01 15:24:31 +0200270 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200271 .slots = pysqlite_NodeType_slots,
272};
273PyTypeObject *pysqlite_NodeType = NULL;
274
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000275static PyMethodDef cache_methods[] = {
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000276 {"get", (PyCFunction)pysqlite_cache_get, METH_O,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000277 PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")},
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000278 {"display", (PyCFunction)pysqlite_cache_display, METH_NOARGS,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000279 PyDoc_STR("For debugging only.")},
280 {NULL, NULL}
281};
282
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200283static PyType_Slot pysqlite_CacheType_slots[] = {
284 {Py_tp_dealloc, pysqlite_cache_dealloc},
285 {Py_tp_methods, cache_methods},
286 {Py_tp_new, PyType_GenericNew},
287 {Py_tp_init, pysqlite_cache_init},
288 {0, NULL},
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000289};
290
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200291static PyType_Spec pysqlite_CacheType_spec = {
292 .name = MODULE_NAME ".Cache",
293 .basicsize = sizeof(pysqlite_Cache),
Erlend Egeberg Aasland9031bd42020-10-01 15:24:31 +0200294 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200295 .slots = pysqlite_CacheType_slots,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000296};
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200297PyTypeObject *pysqlite_CacheType = NULL;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000298
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200299extern int pysqlite_cache_setup_types(PyObject *mod)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000300{
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200301 pysqlite_NodeType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &pysqlite_NodeType_spec, NULL);
302 if (pysqlite_NodeType == NULL) {
303 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000304 }
305
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200306 pysqlite_CacheType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &pysqlite_CacheType_spec, NULL);
307 if (pysqlite_CacheType == NULL) {
308 return -1;
309 }
310 return 0;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000311}