blob: 078a484b86cee6e18faaaaebfa191e055b149b9c [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
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +010037 node->key = Py_NewRef(key);
38 node->data = Py_NewRef(data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000039
40 node->prev = NULL;
41 node->next = NULL;
42
43 return node;
44}
45
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000046void pysqlite_node_dealloc(pysqlite_Node* self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047{
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020048 PyTypeObject *tp = Py_TYPE(self);
49
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000050 Py_DECREF(self->key);
51 Py_DECREF(self->data);
52
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020053 tp->tp_free(self);
54 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000055}
56
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000057int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000058{
59 PyObject* factory;
60 int size = 10;
61
62 self->factory = NULL;
63
Thomas Wouters477c8d52006-05-27 19:21:47 +000064 if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) {
65 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000066 }
67
Thomas Wouters477c8d52006-05-27 19:21:47 +000068 /* minimum cache size is 5 entries */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000069 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
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +010081 self->factory = Py_NewRef(factory);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000082
83 self->decref_factory = 1;
84
85 return 0;
86}
87
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000088void pysqlite_cache_dealloc(pysqlite_Cache* self)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000089{
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +020090 PyTypeObject *tp = Py_TYPE(self);
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000091 pysqlite_Node* node;
92 pysqlite_Node* delete_node;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000093
94 if (!self->factory) {
95 /* constructor failed, just get out of here */
96 return;
97 }
98
Thomas Wouters477c8d52006-05-27 19:21:47 +000099 /* iterate over all nodes and deallocate them */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000100 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
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200112 tp->tp_free(self);
113 Py_DECREF(tp);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000114}
115
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200116PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* key)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000117{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000118 pysqlite_Node* node;
119 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000120 PyObject* data;
121
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200122 node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000123 if (node) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000124 /* 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 */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000132 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 }
Serhiy Storchakafc662ac2018-12-10 16:06:08 +0200160 }
161 else if (PyErr_Occurred()) {
162 return NULL;
163 }
164 else {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000165 /* 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
Serhiy Storchaka5ab81d72016-12-16 16:18:57 +0200169 if (PyDict_GET_SIZE(self->mapping) == self->size) {
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000170 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
Petr Viktorinffd97532020-02-11 17:46:57 +0100187 /* We cannot replace this by PyObject_CallOneArg() since
Jeroen Demeyer196a5302019-07-04 12:31:34 +0200188 * PyObject_CallFunction() has a special case when using a
189 * single tuple as argument. */
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000190 data = PyObject_CallFunction(self->factory, "O", key);
191
192 if (!data) {
193 return NULL;
194 }
195
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000196 node = pysqlite_new_node(key, data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000197 if (!node) {
198 return NULL;
199 }
200 node->prev = self->last;
201
202 Py_DECREF(data);
203
204 if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) {
205 Py_DECREF(node);
206 return NULL;
207 }
208
209 if (self->last) {
210 self->last->next = node;
211 } else {
212 self->first = node;
213 }
214 self->last = node;
215 }
216
Erlend Egeberg Aaslandbf64d902020-12-27 12:05:33 +0100217 return Py_NewRef(node->data);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000218}
219
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000220PyObject* pysqlite_cache_display(pysqlite_Cache* self, PyObject* args)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000221{
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000222 pysqlite_Node* ptr;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000223 PyObject* prevkey;
224 PyObject* nextkey;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000225 PyObject* display_str;
226
227 ptr = self->first;
228
229 while (ptr) {
230 if (ptr->prev) {
231 prevkey = ptr->prev->key;
232 } else {
233 prevkey = Py_None;
234 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000235
236 if (ptr->next) {
237 nextkey = ptr->next->key;
238 } else {
239 nextkey = Py_None;
240 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000241
Amaury Forgeot d'Arc864741b2011-11-06 15:10:48 +0100242 display_str = PyUnicode_FromFormat("%S <- %S -> %S\n",
243 prevkey, ptr->key, nextkey);
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000244 if (!display_str) {
245 return NULL;
246 }
247 PyObject_Print(display_str, stdout, Py_PRINT_RAW);
248 Py_DECREF(display_str);
249
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000250 ptr = ptr->next;
251 }
252
Berker Peksagfe21de92016-04-09 07:34:39 +0300253 Py_RETURN_NONE;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000254}
255
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100256static PyType_Slot node_slots[] = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200257 {Py_tp_dealloc, pysqlite_node_dealloc},
258 {Py_tp_new, PyType_GenericNew},
259 {0, NULL},
260};
261
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100262static PyType_Spec node_spec = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200263 .name = MODULE_NAME ".Node",
264 .basicsize = sizeof(pysqlite_Node),
Erlend Egeberg Aasland9031bd42020-10-01 15:24:31 +0200265 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100266 .slots = node_slots,
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200267};
268PyTypeObject *pysqlite_NodeType = NULL;
269
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000270static PyMethodDef cache_methods[] = {
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000271 {"get", (PyCFunction)pysqlite_cache_get, METH_O,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000272 PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")},
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000273 {"display", (PyCFunction)pysqlite_cache_display, METH_NOARGS,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000274 PyDoc_STR("For debugging only.")},
275 {NULL, NULL}
276};
277
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100278static PyType_Slot cache_slots[] = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200279 {Py_tp_dealloc, pysqlite_cache_dealloc},
280 {Py_tp_methods, cache_methods},
281 {Py_tp_new, PyType_GenericNew},
282 {Py_tp_init, pysqlite_cache_init},
283 {0, NULL},
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000284};
285
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100286static PyType_Spec cache_spec = {
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200287 .name = MODULE_NAME ".Cache",
288 .basicsize = sizeof(pysqlite_Cache),
Erlend Egeberg Aasland9031bd42020-10-01 15:24:31 +0200289 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100290 .slots = cache_slots,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000291};
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200292PyTypeObject *pysqlite_CacheType = NULL;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000293
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200294extern int pysqlite_cache_setup_types(PyObject *mod)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000295{
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100296 pysqlite_NodeType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &node_spec, NULL);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200297 if (pysqlite_NodeType == NULL) {
298 return -1;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000299 }
300
Erlend Egeberg Aasland2ffba2a2020-11-17 13:52:54 +0100301 pysqlite_CacheType = (PyTypeObject *)PyType_FromModuleAndSpec(mod, &cache_spec, NULL);
Erlend Egeberg Aaslanda937ab42020-09-27 14:14:50 +0200302 if (pysqlite_CacheType == NULL) {
303 return -1;
304 }
305 return 0;
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000306}