blob: fd4e619f6a0115412ab03bac8bc4736562266b4b [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{
Miss Islington (bot)ff359d72021-05-31 02:12:27 -070050 Py_VISIT(Py_TYPE(self));
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -070051 Py_VISIT(self->key);
52 Py_VISIT(self->data);
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -070053 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{
Miss Islington (bot)ff359d72021-05-31 02:12:27 -0700109 Py_VISIT(Py_TYPE(self));
110 Py_VISIT(self->mapping);
111 if (self->decref_factory) {
112 Py_VISIT(self->factory);
113 }
114
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700115 pysqlite_Node *node = self->first;
116 while (node) {
117 Py_VISIT(node);
118 node = node->next;
119 }
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700120 return 0;
121}
122
123static int
124cache_clear(pysqlite_Cache *self)
125{
Miss Islington (bot)ff359d72021-05-31 02:12:27 -0700126 Py_CLEAR(self->mapping);
127 if (self->decref_factory) {
128 Py_CLEAR(self->factory);
129 }
130
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700131 /* iterate over all nodes and deallocate them */
132 pysqlite_Node *node = self->first;
133 self->first = NULL;
134 while (node) {
135 pysqlite_Node *delete_node = node;
136 node = node->next;
137 Py_CLEAR(delete_node);
138 }
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700139 return 0;
140}
141
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +0100142static void
143pysqlite_cache_dealloc(pysqlite_Cache *self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000144{
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000145 if (!self->factory) {
146 /* constructor failed, just get out of here */
147 return;
148 }
149
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700150 PyObject_GC_UnTrack(self);
151 PyTypeObject *tp = Py_TYPE(self);
152 tp->tp_clear((PyObject *)self);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200153 tp->tp_free(self);
154 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000155}
156
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200157PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* key)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000158{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000159 pysqlite_Node* node;
160 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000161 PyObject* data;
162
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200163 node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000164 if (node) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000165 /* an entry for this key already exists in the cache */
166
167 /* increase usage counter of the node found */
168 if (node->count < LONG_MAX) {
169 node->count++;
170 }
171
172 /* if necessary, reorder entries in the cache by swapping positions */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000173 if (node->prev && node->count > node->prev->count) {
174 ptr = node->prev;
175
176 while (ptr->prev && node->count > ptr->prev->count) {
177 ptr = ptr->prev;
178 }
179
180 if (node->next) {
181 node->next->prev = node->prev;
182 } else {
183 self->last = node->prev;
184 }
185 if (node->prev) {
186 node->prev->next = node->next;
187 }
188 if (ptr->prev) {
189 ptr->prev->next = node;
190 } else {
191 self->first = node;
192 }
193
194 node->next = ptr;
195 node->prev = ptr->prev;
196 if (!node->prev) {
197 self->first = node;
198 }
199 ptr->prev = node;
200 }
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200201 }
202 else if (PyErr_Occurred()) {
203 return NULL;
204 }
205 else {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000206 /* There is no entry for this key in the cache, yet. We'll insert a new
207 * entry in the cache, and make space if necessary by throwing the
208 * least used item out of the cache. */
209
Serhiy Storchaka5ab81d72016-12-16 16:18:57 +0200210 if (PyDict_GET_SIZE(self->mapping) == self->size) {
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000211 if (self->last) {
212 node = self->last;
213
214 if (PyDict_DelItem(self->mapping, self->last->key) != 0) {
215 return NULL;
216 }
217
218 if (node->prev) {
219 node->prev->next = NULL;
220 }
221 self->last = node->prev;
222 node->prev = NULL;
223
224 Py_DECREF(node);
225 }
226 }
227
Petr Viktorinffd97532020-02-11 17:46:57 +0100228 /* We cannot replace this by PyObject_CallOneArg() since
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200229 * PyObject_CallFunction() has a special case when using a
230 * single tuple as argument. */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000231 data = PyObject_CallFunction(self->factory, "O", key);
232
233 if (!data) {
234 return NULL;
235 }
236
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000237 node = pysqlite_new_node(key, data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000238 if (!node) {
239 return NULL;
240 }
241 node->prev = self->last;
242
243 Py_DECREF(data);
244
245 if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) {
246 Py_DECREF(node);
247 return NULL;
248 }
249
250 if (self->last) {
251 self->last->next = node;
252 } else {
253 self->first = node;
254 }
255 self->last = node;
256 }
257
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +0100258 return Py_NewRef(node->data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000259}
260
Erlend Egeberg Aaslandbf838a62021-02-21 01:29:19 +0100261static PyObject *
262pysqlite_cache_display(pysqlite_Cache *self, PyObject *args)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000263{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000264 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000265 PyObject* prevkey;
266 PyObject* nextkey;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000267 PyObject* display_str;
268
269 ptr = self->first;
270
271 while (ptr) {
272 if (ptr->prev) {
273 prevkey = ptr->prev->key;
274 } else {
275 prevkey = Py_None;
276 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000277
278 if (ptr->next) {
279 nextkey = ptr->next->key;
280 } else {
281 nextkey = Py_None;
282 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000283
Amaury Forgeot d'Arc864741b2011-11-06 15:10:48 +0100284 display_str = PyUnicode_FromFormat("%S <- %S -> %S\n",
285 prevkey, ptr->key, nextkey);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000286 if (!display_str) {
287 return NULL;
288 }
289 PyObject_Print(display_str, stdout, Py_PRINT_RAW);
290 Py_DECREF(display_str);
291
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000292 ptr = ptr->next;
293 }
294
Berker Peksagfe21de92016-04-09 07:34:39 +0300295 Py_RETURN_NONE;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000296}
297
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100298static PyType_Slot node_slots[] = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200299 {Py_tp_dealloc, pysqlite_node_dealloc},
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700300 {Py_tp_traverse, node_traverse},
301 {Py_tp_clear, node_clear},
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200302 {0, NULL},
303};
304
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100305static PyType_Spec node_spec = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200306 .name = MODULE_NAME ".Node",
307 .basicsize = sizeof(pysqlite_Node),
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700308 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100309 .slots = node_slots,
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200310};
311PyTypeObject *pysqlite_NodeType = NULL;
312
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000313static PyMethodDef cache_methods[] = {
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000314 {"get", (PyCFunction)pysqlite_cache_get, METH_O,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000315 PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")},
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000316 {"display", (PyCFunction)pysqlite_cache_display, METH_NOARGS,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000317 PyDoc_STR("For debugging only.")},
318 {NULL, NULL}
319};
320
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100321static PyType_Slot cache_slots[] = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200322 {Py_tp_dealloc, pysqlite_cache_dealloc},
323 {Py_tp_methods, cache_methods},
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200324 {Py_tp_init, pysqlite_cache_init},
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700325 {Py_tp_traverse, cache_traverse},
326 {Py_tp_clear, cache_clear},
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200327 {0, NULL},
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000328};
329
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100330static PyType_Spec cache_spec = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200331 .name = MODULE_NAME ".Cache",
332 .basicsize = sizeof(pysqlite_Cache),
Miss Islington (bot)e8d9df02021-05-25 11:08:39 -0700333 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100334 .slots = cache_slots,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000335};
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200336PyTypeObject *pysqlite_CacheType = NULL;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000337
Erlend Egeberg Aasland38b6c2a2021-02-21 11:07:49 +0100338int
339pysqlite_cache_setup_types(PyObject *mod)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000340{
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100341 pysqlite_NodeType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &node_spec, NULL);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200342 if (pysqlite_NodeType == NULL) {
343 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000344 }
345
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100346 pysqlite_CacheType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &cache_spec, NULL);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200347 if (pysqlite_CacheType == NULL) {
348 return -1;
349 }
350 return 0;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000351}