############################################################################## # # Copyright (c) 2001, 2002, 2003 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Very Simple Mapping ZODB storage The Mapping storage provides an extremely simple storage implementation that doesn't provide undo or version support. It is meant to illustrate the simplest possible storage. The Mapping storage uses a single data structure to map object ids to data. """ __version__='$Revision: 1.13 $'[11:-2] from ZODB import utils from ZODB import BaseStorage from ZODB import POSException from persistent.TimeStamp import TimeStamp class MappingStorage(BaseStorage.BaseStorage): def __init__(self, name='Mapping Storage'): BaseStorage.BaseStorage.__init__(self, name) self._index = {} self._tindex = [] self._ltid = None # Note: If you subclass this and use a persistent mapping facility # (e.g. a dbm file), you will need to get the maximum key and save it # as self._oid. See dbmStorage. def __len__(self): return len(self._index) def getSize(self): # These constants are for Python object memory overheads s = 32 for oid in self._index.keys(): p = self._index[oid] s += 56 + len(p) return s def load(self, oid, version): self._lock_acquire() try: p = self._index[oid] return p[8:], p[:8] # pickle, serial finally: self._lock_release() def loadEx(self, oid, version): self._lock_acquire() try: # Since this storage doesn't support versions, tid and # serial will always be the same. p = self._index[oid] return p[8:], p[:8], "" # pickle, tid, version finally: self._lock_release() def getTid(self, oid): self._lock_acquire() try: # The tid is the first 8 bytes of the buffer. s = self._index[oid] return s[:8] finally: self._lock_release() def store(self, oid, serial, data, version, transaction): if transaction is not self._transaction: raise POSException.StorageTransactionError(self, transaction) if version: raise POSException.Unsupported, "Versions aren't supported" self._lock_acquire() try: if self._index.has_key(oid): old = self._index[oid] oserial = old[:8] if serial != oserial: raise POSException.ConflictError(oid=oid, serials=(oserial, serial), data=data) self._tindex.append((oid, self._tid + data)) finally: self._lock_release() return self._tid def _clear_temp(self): self._tindex = [] def _finish(self, tid, user, desc, ext): for oid, p in self._tindex: self._index[oid] = p self._ltid = self._tid def lastTransaction(self): return self._ltid def pack(self, t, referencesf): self._lock_acquire() try: if not self._index: return # Build an index of *only* those objects reachable from the root. rootl = ['\0\0\0\0\0\0\0\0'] pindex = {} while rootl: oid = rootl.pop() if pindex.has_key(oid): continue # Scan non-version pickle for references r = self._index[oid] pindex[oid] = r p = r[8:] referencesf(p, rootl) # Now delete any unreferenced entries: for oid in self._index.keys(): if not pindex.has_key(oid): del self._index[oid] finally: self._lock_release() def _splat(self): """Spit out a string showing state.""" o = [] o.append('Index:') keys = self._index.keys() keys.sort() for oid in keys: r = self._index[oid] o.append(' %s: %s, %s' % (utils.u64(oid),TimeStamp(r[:8]),`r[8:]`)) return '\n'.join(o)