blob: 324f9288a927a93a86e56008552673f3adeb8a4a [file] [log] [blame]
Guido van Rossum79ae53e1995-07-18 18:23:44 +00001/***********************************************************
2Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
3The Netherlands.
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25/* Berkeley DB hash interface.
26 Author: Michael McLay
27 Hacked: Guido van Rossum
28
29 XXX To do:
30 - provide interface to the B-tree and record libraries too
31 - provide a way to access the various hash functions
32 - support more open flags
33 */
34
35#include "allobjects.h"
36#include "modsupport.h"
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <db.h>
42/* Please don't include internal header files of the Berkeley db package
43 (it messes up the info required in the Setup file) */
44
45typedef struct {
46 OB_HEAD
47 DB *di_dbhash;
48 int di_size; /* -1 means recompute */
49} dbhashobject;
50
51staticforward typeobject Dbhashtype;
52
53#define is_dbhashobject(v) ((v)->ob_type == &Dbhashtype)
54
55static object *DbhashError;
56
57static object *
58newdbhashobject(file, flags, mode,
59 bsize, ffactor, nelem, cachesize, hash, lorder)
60 char *file;
61 int flags;
62 int mode;
63 int bsize;
64 int ffactor;
65 int nelem;
66 int cachesize;
67 int hash; /* XXX ignored */
68 int lorder;
69{
70 dbhashobject *dp;
71 HASHINFO info;
72
73 if ((dp = NEWOBJ(dbhashobject, &Dbhashtype)) == NULL)
74 return NULL;
75
76 info.bsize = bsize;
77 info.ffactor = ffactor;
78 info.nelem = nelem;
79 info.cachesize = cachesize;
80 info.hash = NULL; /* XXX should derive from hash argument */
81 info.lorder = lorder;
82
83 if ((dp->di_dbhash = dbopen(file, flags, mode, DB_HASH, &info)) == NULL) {
84 err_errno(DbhashError);
85 DECREF(dp);
86 return NULL;
87 }
88
89 dp->di_size = -1;
90
91 return (object *)dp;
92}
93
94static object *
95dbhash_close(dp)
96 dbhashobject *dp;
97{
98 if (dp->di_dbhash != NULL) {
99 if ((dp->di_dbhash->close)(dp->di_dbhash) != 0) {
100 err_errno(DbhashError);
101 return NULL;
102 }
103 }
104 dp->di_dbhash = NULL;
105 INCREF(None);
106 return None;
107}
108
109static void
110dbhash_dealloc(dp)
111 dbhashobject *dp;
112{
113 if (dp->di_dbhash != NULL) {
114 if ((dp->di_dbhash->close)(dp->di_dbhash) != 0)
115 fprintf(stderr,
116 "Python dbhash: close errno %s in dealloc\n", errno);
117 }
118 DEL(dp);
119}
120
121static int
122dbhash_length(dp)
123 dbhashobject *dp;
124{
125 if (dp->di_size < 0) {
126 DBT krec, drec;
127 int status;
128 int size = 0;
129 for (status = (dp->di_dbhash->seq)(dp->di_dbhash, &krec, &drec,R_FIRST);
130 status == 0;
131 status = (dp->di_dbhash->seq)(dp->di_dbhash, &krec, &drec, R_NEXT))
132 size++;
133 if (status < 0) {
134 err_errno(DbhashError);
135 return -1;
136 }
137 dp->di_size = size;
138 }
139 return dp->di_size;
140}
141
142static object *
143dbhash_subscript(dp, key)
144 dbhashobject *dp;
145 object *key;
146{
147 int status;
148 object *v;
149 DBT krec, drec;
150 char *data;
151 int size;
152
153 if (!getargs(key, "s#", &data, &size))
154 return NULL;
155 krec.data = data;
156 krec.size = size;
157
158 status = (dp->di_dbhash->get)(dp->di_dbhash, &krec, &drec, 0);
159 if (status != 0) {
160 if (status < 0)
161 err_errno(DbhashError);
162 else
163 err_setval(KeyError, key);
164 return NULL;
165 }
166
167 return newsizedstringobject((char *)drec.data, (int)drec.size);
168}
169
170static int
171dbhash_ass_sub(dp, key, value)
172 dbhashobject *dp;
173 object *key, *value;
174{
175 int status;
176 DBT krec, drec;
177 char *data;
178 int size;
179
180 if (!getargs(key, "s#", &data, &size)) {
181 err_setstr(TypeError, "dbhash key type must be string");
182 return -1;
183 }
184 krec.data = data;
185 krec.size = size;
186 dp->di_size = -1;
187 if (value == NULL) {
188 status = (dp->di_dbhash->del)(dp->di_dbhash, &krec, 0);
189 }
190 else {
191 if (!getargs(value, "s#", &data, &size)) {
192 err_setstr(TypeError, "dbhash value type must be string");
193 return -1;
194 }
195 drec.data = data;
196 drec.size = size;
197 status = (dp->di_dbhash->put)(dp->di_dbhash, &krec, &drec, 0);
198 }
199 if (status != 0) {
200 if (status < 0)
201 err_errno(DbhashError);
202 else
203 err_setval(KeyError, key);
204 return -1;
205 }
206 return 0;
207}
208
209static mapping_methods dbhash_as_mapping = {
210 (inquiry)dbhash_length, /*mp_length*/
211 (binaryfunc)dbhash_subscript, /*mp_subscript*/
212 (objobjargproc)dbhash_ass_sub, /*mp_ass_subscript*/
213};
214
215static object *
216dbhash_keys(dp, args)
217 dbhashobject *dp;
218 object *args;
219{
220 object *list, *item;
221 DBT krec, drec;
222 int status;
223 int err;
224
225 if (dp == NULL || !is_dbhashobject(dp)) {
226 err_badcall();
227 return NULL;
228 }
229 if (!getnoarg(args))
230 return NULL;
231 list = newlistobject(0);
232 if (list == NULL)
233 return NULL;
234 for (status = (dp->di_dbhash->seq)(dp->di_dbhash, &krec, &drec, R_FIRST);
235 status == 0;
236 status = (dp->di_dbhash->seq)(dp->di_dbhash, &krec, &drec, R_NEXT)) {
237 item = newsizedstringobject((char *)krec.data, (int)krec.size);
238 if (item == NULL) {
239 DECREF(list);
240 return NULL;
241 }
242 err = addlistitem(list, item);
243 DECREF(item);
244 if (err != 0) {
245 DECREF(list);
246 return NULL;
247 }
248 }
249 if (status < 0) {
250 err_errno(DbhashError);
251 DECREF(list);
252 return NULL;
253 }
254 if (dp->di_size < 0)
255 dp->di_size = getlistsize(list); /* We just did the work for this... */
256 return list;
257}
258
259static object *
260dbhash_has_key(dp, args)
261 dbhashobject *dp;
262 object *args;
263{
264 DBT krec, drec;
265 int status;
266 char *data;
267 int size;
268
269 if (!getargs(args, "s#", &data, &size))
270 return NULL;
271 krec.data = data;
272 krec.size = size;
273
274 status = (dp->di_dbhash->get)(dp->di_dbhash, &krec, &drec, 0);
275 if (status < 0) {
276 err_errno(DbhashError);
277 return NULL;
278 }
279
280 return newintobject(status == 0);
281}
282
283static struct methodlist dbhash_methods[] = {
284 {"close", (method)dbhash_close},
285 {"keys", (method)dbhash_keys},
286 {"has_key", (method)dbhash_has_key},
287 {NULL, NULL} /* sentinel */
288};
289
290static object *
291dbhash_getattr(dp, name)
292 object *dp;
293 char *name;
294{
295 return findmethod(dbhash_methods, dp, name);
296}
297
298static typeobject Dbhashtype = {
299 OB_HEAD_INIT(&Typetype)
300 0,
301 "dbhash",
302 sizeof(dbhashobject),
303 0,
304 (destructor)dbhash_dealloc, /*tp_dealloc*/
305 0, /*tp_print*/
306 (getattrfunc)dbhash_getattr, /*tp_getattr*/
307 0, /*tp_setattr*/
308 0, /*tp_compare*/
309 0, /*tp_repr*/
310 0, /*tp_as_number*/
311 0, /*tp_as_sequence*/
312 &dbhash_as_mapping, /*tp_as_mapping*/
313};
314
315static object *
316dbhashopen(self, args)
317 object *self;
318 object *args;
319{
320 char *file;
321 char *flag = NULL;
322 int flags = O_RDONLY;
323 int mode = 0666;
324 int bsize = 0;
325 int ffactor = 0;
326 int nelem = 0;
327 int cachesize = 0;
328 int hash = 0; /* XXX currently ignored */
329 int lorder = 0;
330
331 if (!newgetargs(args, "s|siiiiiii",
332 &file, &flag, &mode,
333 &bsize, &ffactor, &nelem, &cachesize, &hash, &lorder))
334 return NULL;
335 if (flag != NULL) {
336 /* XXX need a way to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */
337 if (strcmp(flag, "r") == 0)
338 flags = O_RDONLY;
339 else if (strcmp(flag, "w") == 0)
340 flags = O_RDWR;
341 else if (strcmp(flag, "c") == 0)
342 flags = O_RDWR|O_CREAT;
343 else if (strcmp(flag, "n") == 0)
344 flags = O_RDWR|O_CREAT|O_TRUNC;
345 else {
346 err_setstr(DbhashError,
347 "Flag should be one of 'r', 'w', 'c' or 'n'");
348 return NULL;
349 }
350 }
351 return newdbhashobject(file, flags, mode,
352 bsize, ffactor, nelem, cachesize, hash, lorder);
353}
354
355static struct methodlist dbhashmodule_methods[] = {
356 {"open", (method)dbhashopen, 1},
357 {0, 0},
358};
359
360void
361initdbhash() {
362 object *m, *d;
363
364 m = initmodule("dbhash", dbhashmodule_methods);
365 d = getmoduledict(m);
366 DbhashError = newstringobject("dbhash.error");
367 if (DbhashError == NULL || dictinsert(d, "error", DbhashError))
368 fatal("can't define dbhash.error");
369}