blob: e0a1707348c912cfde477cc67d53bf060206cd75 [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 */
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +010028static pysqlite_Node *
29pysqlite_new_node(PyObject *key, PyObject *data)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000030{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000031 pysqlite_Node* node;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000032
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020033 node = (pysqlite_Node*) (pysqlite_NodeType->tp_alloc(pysqlite_NodeType, 0));
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000034 if (!node) {
35 return NULL;
36 }
37
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +010038 node->key = Py_NewRef(key);
39 node->data = Py_NewRef(data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000040
41 node->prev = NULL;
42 node->next = NULL;
43
44 return node;
45}
46
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -070047static int
48node_traverse(pysqlite_Node *self, visitproc visit, void *arg)
49{
50 Py_VISIT(self->key);
51 Py_VISIT(self->data);
52 Py_VISIT(Py_TYPE(self));
53 return 0;
54}
55
56static int
57node_clear(pysqlite_Node *self)
58{
59 Py_CLEAR(self->key);
60 Py_CLEAR(self->data);
61 return 0;
62}
63
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +010064static void
65pysqlite_node_dealloc(pysqlite_Node *self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000066{
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020067 PyTypeObject *tp = Py_TYPE(self);
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -070068 PyObject_GC_UnTrack(self);
69 tp->tp_clear((PyObject *)self);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020070 tp->tp_free(self);
71 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000072}
73
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +010074static int
75pysqlite_cache_init(pysqlite_Cache *self, PyObject *args, PyObject *kwargs)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000076{
77 PyObject* factory;
78 int size = 10;
79
80 self->factory = NULL;
81
Thomas Wouters477c8d52006-05-27 19:21:47 +000082 if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) {
83 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000084 }
85
Thomas Wouters477c8d52006-05-27 19:21:47 +000086 /* minimum cache size is 5 entries */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000087 if (size < 5) {
88 size = 5;
89 }
90 self->size = size;
91 self->first = NULL;
92 self->last = NULL;
93
94 self->mapping = PyDict_New();
95 if (!self->mapping) {
96 return -1;
97 }
98
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +010099 self->factory = Py_NewRef(factory);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000100
101 self->decref_factory = 1;
102
103 return 0;
104}
105
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700106static int
107cache_traverse(pysqlite_Cache *self, visitproc visit, void *arg)
108{
109 pysqlite_Node *node = self->first;
110 while (node) {
111 Py_VISIT(node);
112 node = node->next;
113 }
114 Py_VISIT(self->mapping);
115 if (self->decref_factory) {
116 Py_VISIT(self->factory);
117 }
118 Py_VISIT(Py_TYPE(self));
119 return 0;
120}
121
122static int
123cache_clear(pysqlite_Cache *self)
124{
125 /* iterate over all nodes and deallocate them */
126 pysqlite_Node *node = self->first;
127 self->first = NULL;
128 while (node) {
129 pysqlite_Node *delete_node = node;
130 node = node->next;
131 Py_CLEAR(delete_node);
132 }
133 if (self->decref_factory) {
134 Py_CLEAR(self->factory);
135 }
136 Py_CLEAR(self->mapping);
137 return 0;
138}
139
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +0100140static void
141pysqlite_cache_dealloc(pysqlite_Cache *self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000142{
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000143 if (!self->factory) {
144 /* constructor failed, just get out of here */
145 return;
146 }
147
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700148 PyObject_GC_UnTrack(self);
149 PyTypeObject *tp = Py_TYPE(self);
150 tp->tp_clear((PyObject *)self);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200151 tp->tp_free(self);
152 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000153}
154
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200155PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* key)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000156{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000157 pysqlite_Node* node;
158 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000159 PyObject* data;
160
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200161 node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000162 if (node) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000163 /* an entry for this key already exists in the cache */
164
165 /* increase usage counter of the node found */
166 if (node->count < LONG_MAX) {
167 node->count++;
168 }
169
170 /* if necessary, reorder entries in the cache by swapping positions */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000171 if (node->prev && node->count > node->prev->count) {
172 ptr = node->prev;
173
174 while (ptr->prev && node->count > ptr->prev->count) {
175 ptr = ptr->prev;
176 }
177
178 if (node->next) {
179 node->next->prev = node->prev;
180 } else {
181 self->last = node->prev;
182 }
183 if (node->prev) {
184 node->prev->next = node->next;
185 }
186 if (ptr->prev) {
187 ptr->prev->next = node;
188 } else {
189 self->first = node;
190 }
191
192 node->next = ptr;
193 node->prev = ptr->prev;
194 if (!node->prev) {
195 self->first = node;
196 }
197 ptr->prev = node;
198 }
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200199 }
200 else if (PyErr_Occurred()) {
201 return NULL;
202 }
203 else {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000204 /* There is no entry for this key in the cache, yet. We'll insert a new
205 * entry in the cache, and make space if necessary by throwing the
206 * least used item out of the cache. */
207
Serhiy Storchaka5ab81d72016-12-16 16:18:57 +0200208 if (PyDict_GET_SIZE(self->mapping) == self->size) {
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000209 if (self->last) {
210 node = self->last;
211
212 if (PyDict_DelItem(self->mapping, self->last->key) != 0) {
213 return NULL;
214 }
215
216 if (node->prev) {
217 node->prev->next = NULL;
218 }
219 self->last = node->prev;
220 node->prev = NULL;
221
222 Py_DECREF(node);
223 }
224 }
225
Petr Viktorinffd97532020-02-11 17:46:57 +0100226 /* We cannot replace this by PyObject_CallOneArg() since
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200227 * PyObject_CallFunction() has a special case when using a
228 * single tuple as argument. */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000229 data = PyObject_CallFunction(self->factory, "O", key);
230
231 if (!data) {
232 return NULL;
233 }
234
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000235 node = pysqlite_new_node(key, data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000236 if (!node) {
237 return NULL;
238 }
239 node->prev = self->last;
240
241 Py_DECREF(data);
242
243 if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) {
244 Py_DECREF(node);
245 return NULL;
246 }
247
248 if (self->last) {
249 self->last->next = node;
250 } else {
251 self->first = node;
252 }
253 self->last = node;
254 }
255
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +0100256 return Py_NewRef(node->data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000257}
258
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +0100259static PyObject *
260pysqlite_cache_display(pysqlite_Cache *self, PyObject *args)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000261{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000262 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000263 PyObject* prevkey;
264 PyObject* nextkey;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000265 PyObject* display_str;
266
267 ptr = self->first;
268
269 while (ptr) {
270 if (ptr->prev) {
271 prevkey = ptr->prev->key;
272 } else {
273 prevkey = Py_None;
274 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000275
276 if (ptr->next) {
277 nextkey = ptr->next->key;
278 } else {
279 nextkey = Py_None;
280 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000281
Amaury Forgeot d'Arc864741b2011-11-06 15:10:48 +0100282 display_str = PyUnicode_FromFormat("%S <- %S -> %S\n",
283 prevkey, ptr->key, nextkey);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000284 if (!display_str) {
285 return NULL;
286 }
287 PyObject_Print(display_str, stdout, Py_PRINT_RAW);
288 Py_DECREF(display_str);
289
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000290 ptr = ptr->next;
291 }
292
Berker Peksagfe21de92016-04-09 07:34:39 +0300293 Py_RETURN_NONE;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000294}
295
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100296static PyType_Slot node_slots[] = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200297 {Py_tp_dealloc, pysqlite_node_dealloc},
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700298 {Py_tp_traverse, node_traverse},
299 {Py_tp_clear, node_clear},
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200300 {0, NULL},
301};
302
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100303static PyType_Spec node_spec = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200304 .name = MODULE_NAME ".Node",
305 .basicsize = sizeof(pysqlite_Node),
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700306 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100307 .slots = node_slots,
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200308};
309PyTypeObject *pysqlite_NodeType = NULL;
310
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000311static PyMethodDef cache_methods[] = {
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000312 {"get", (PyCFunction)pysqlite_cache_get, METH_O,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000313 PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")},
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000314 {"display", (PyCFunction)pysqlite_cache_display, METH_NOARGS,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000315 PyDoc_STR("For debugging only.")},
316 {NULL, NULL}
317};
318
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100319static PyType_Slot cache_slots[] = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200320 {Py_tp_dealloc, pysqlite_cache_dealloc},
321 {Py_tp_methods, cache_methods},
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200322 {Py_tp_init, pysqlite_cache_init},
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700323 {Py_tp_traverse, cache_traverse},
324 {Py_tp_clear, cache_clear},
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200325 {0, NULL},
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000326};
327
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100328static PyType_Spec cache_spec = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200329 .name = MODULE_NAME ".Cache",
330 .basicsize = sizeof(pysqlite_Cache),
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700331 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100332 .slots = cache_slots,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000333};
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200334PyTypeObject *pysqlite_CacheType = NULL;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000335
Erlend Egeberg Aasland38b6c2a2021-02-21 11:07:49 +0100336int
337pysqlite_cache_setup_types(PyObject *mod)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000338{
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100339 pysqlite_NodeType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &node_spec, NULL);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200340 if (pysqlite_NodeType == NULL) {
341 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000342 }
343
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100344 pysqlite_CacheType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &cache_spec, NULL);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200345 if (pysqlite_CacheType == NULL) {
346 return -1;
347 }
348 return 0;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000349}