1 """A slow but simple dbm clone for the Mac.
3 For database spam, spam.dir contains the index (a text file),
4 spam.bak *may* contain a backup of the index (also a text file),
5 while spam.dat contains the data (a binary file).
9 - reclaim free space (currently, space once occupied by deleted or expanded
10 items is never reused)
12 - support concurrent access (currently, if two processes take turns making
13 updates, they can mess up the index)
15 - support efficient access to large databases (currently, the whole index
16 is read when the database is opened, and some updates rewrite the whole index)
18 - support opening for read-only (flag = 'm')
22 _os
= __import__('os')
25 _open
= __builtin__
.open
31 def __init__(self
, file):
32 self
._dirfile
= file + '.dir'
33 self
._datfile
= file + '.dat'
34 self
._bakfile
= file + '.bak'
35 # Mod by Jack: create data file if needed
37 f
= _open(self
._datfile
, 'r')
39 f
= _open(self
._datfile
, 'w')
46 f
= _open(self
._dirfile
)
53 key
, (pos
, siz
) = eval(line
)
54 self
._index
[key
] = (pos
, siz
)
58 try: _os
.unlink(self
._bakfile
)
59 except _os
.error
: pass
60 try: _os
.rename(self
._dirfile
, self
._bakfile
)
61 except _os
.error
: pass
62 f
= _open(self
._dirfile
, 'w')
63 for key
, (pos
, siz
) in self
._index
.items():
64 f
.write("%s, (%s, %s)\n" % (`key`
, `pos`
, `siz`
))
67 def __getitem__(self
, key
):
68 pos
, siz
= self
._index
[key
] # may raise KeyError
69 f
= _open(self
._datfile
, 'rb')
75 def _addval(self
, val
):
76 f
= _open(self
._datfile
, 'rb+')
79 ## Does not work under MW compiler
80 ## pos = ((pos + _BLOCKSIZE - 1) / _BLOCKSIZE) * _BLOCKSIZE
82 npos
= ((pos
+ _BLOCKSIZE
- 1) / _BLOCKSIZE
) * _BLOCKSIZE
83 f
.write('\0'*(npos
-pos
))
88 return (pos
, len(val
))
90 def _setval(self
, pos
, val
):
91 f
= _open(self
._datfile
, 'rb+')
97 def _addkey(self
, key
, (pos
, siz
)):
98 self
._index
[key
] = (pos
, siz
)
99 f
= _open(self
._dirfile
, 'a')
100 f
.write("%s, (%s, %s)\n" % (`key`
, `pos`
, `siz`
))
103 def __setitem__(self
, key
, val
):
104 if not type(key
) == type('') == type(val
):
105 raise TypeError, "dbmac keys and values must be strings"
106 if not self
._index
.has_key(key
):
107 (pos
, siz
) = self
._addval
(val
)
108 self
._addkey
(key
, (pos
, siz
))
110 pos
, siz
= self
._index
[key
]
111 oldblocks
= (siz
+ _BLOCKSIZE
- 1) / _BLOCKSIZE
112 newblocks
= (len(val
) + _BLOCKSIZE
- 1) / _BLOCKSIZE
113 if newblocks
<= oldblocks
:
114 pos
, siz
= self
._setval
(pos
, val
)
115 self
._index
[key
] = pos
, siz
117 pos
, siz
= self
._addval
(val
)
118 self
._index
[key
] = pos
, siz
119 self
._addkey
(key
, (pos
, siz
))
121 def __delitem__(self
, key
):
126 return self
._index
.keys()
128 def has_key(self
, key
):
129 return self
._index
.has_key(key
)
132 return len(self
._index
)
135 self
._index
= self
._datfile
= self
._dirfile
= self
._bakfile
= None
138 def open(file, flag
= None, mode
= None):
139 # flag, mode arguments are currently ignored
140 return _Database(file)