Path: blob/master/sage/databases/compressed_storage.py
6915 views
"""1Compression for ZODB.2"""34#*****************************************************************************5#6# Sage: System for Algebra and Geometry Experimentation7#8# Copyright (C) 2005 William Stein <[email protected]>9#10# Distributed under the terms of the GNU General Public License (GPL)11#12# This code is distributed in the hope that it will be useful,13# but WITHOUT ANY WARRANTY; without even the implied warranty of14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU15# General Public License for more details.16#17# The full text of the GPL is available at:18#19# http://www.gnu.org/licenses/20###############################################################################21#22# This code is a modified version of the code I got here:23#24# http://www.zope.org/Members/tsarna/CompressedStorage25#26# I modified it (very slightly) to work with the new ZODB...27#*****************************************************************************282930# This is the original copyright notice:31#32# $Id$33#34# Copyright (c) 1999 Tyler C. Sarna ([email protected])35# All rights reserved.36#37# Redistribution and use in source and binary forms, with or without38# modification, are permitted provided that the following conditions39# are met:40# 1. Redistributions of source code must retain the above copyright41# notice, this list of conditions and the following disclaimer.42# 2. Redistributions in binary form must reproduce the above copyright43# notice, this list of conditions and the following disclaimer in the44# documentation and/or other materials provided with the distribution.45# 3. The name of the author may not be used to endorse or promote products46# derived from this software without specific prior written permission47#48# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR49# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES50# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.51# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,52# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT53# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,54# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY55# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT56# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF57# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.58#5960"""61Compressed Layered Storage6263This is a storage that layers over another storage, compressing64objects larger than a certain threshold (default 2K).6566The format of compressed pickles is an optional prefix byte followed by67data. If the prefix byte is 1, the data is compressed. If the68prefix byte is 0, the data is uncompressed. If the first byte is not 069or 1, the data is assumed uncompressed. Since python pickles never70start with 0 or 1, they are stored directly without a prefix byte. This71makes compatibility with existing storages easier -- you can simply72stick a CompressedStorage atop an existing storage and it will work.7374However, if some other layered storage sits atop this one, say an75encrypting storage, the "pickle" this layer receives may start with a 076or 1. In that event, if this layer won't be compressing it, a 0 byte77will be prefixed to assure that CompressedStorage doesn't try to78decompress data that isn't compressed.7980"""8182import bz2838485# The following would only be used for pack, and pack doesn't work...86class ZWrap:87"""88Wrap a function to produce a new one that can take a compressed89string as the first argument.90"""9192def __init__(self, f):93self.func = f9495def __call__(self, *args, **kw):96if type(args) != type(()):97args = tuple(args)98if args[0][0] == '\1':99args = (bz2.decompress(args[0][1:]),) + args[1:]100elif args[0][1] == '\0':101args = (args[0][1:],) + args[1:]102return apply(self.func, args, kw)103104105class CompressedStorage:106def __init__(self, base, thresh=2048):107self._base = base108self._zthresh = thresh109110def __getattr__(self, attr):111"""112Pseudo-acquisition for base's stuff that we don't113otherwise override.114"""115return getattr(self._base, attr)116117def load(self, oid, version):118p, serial = self._base.load(oid, version)119if p[0] == '\1':120p = bz2.decompress(p[1:])121elif p[0] == '\0':122p = p[1:]123return p, serial124125def store(self, oid, serial, data, version, transaction):126if self._is_read_only:127raise POSException.ReadOnlyError()128datalen = len(data)129compressed = 0130if datalen >= self._zthresh:131ndata = '\1' + bz2.compress(data)132# print datalen - len(ndata), datalen, len(ndata)133if len(ndata) < datalen:134data = ndata135compressed = 1136137if not compressed and data[0] in '\0\1':138data = '\0' + data139140return self._base.store(oid, serial, data, version, transaction)141142def pack(self, t, referencesf):143return self._base.pack(t, ZWrap(referencesf))144145146