Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/databases/compressed_storage.py
6915 views
1
"""
2
Compression for ZODB.
3
"""
4
5
#*****************************************************************************
6
#
7
# Sage: System for Algebra and Geometry Experimentation
8
#
9
# Copyright (C) 2005 William Stein <[email protected]>
10
#
11
# Distributed under the terms of the GNU General Public License (GPL)
12
#
13
# This code is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
# General Public License for more details.
17
#
18
# The full text of the GPL is available at:
19
#
20
# http://www.gnu.org/licenses/
21
###############################################################################
22
#
23
# This code is a modified version of the code I got here:
24
#
25
# http://www.zope.org/Members/tsarna/CompressedStorage
26
#
27
# I modified it (very slightly) to work with the new ZODB...
28
#*****************************************************************************
29
30
31
# This is the original copyright notice:
32
#
33
# $Id$
34
#
35
# Copyright (c) 1999 Tyler C. Sarna ([email protected])
36
# All rights reserved.
37
#
38
# Redistribution and use in source and binary forms, with or without
39
# modification, are permitted provided that the following conditions
40
# are met:
41
# 1. Redistributions of source code must retain the above copyright
42
# notice, this list of conditions and the following disclaimer.
43
# 2. Redistributions in binary form must reproduce the above copyright
44
# notice, this list of conditions and the following disclaimer in the
45
# documentation and/or other materials provided with the distribution.
46
# 3. The name of the author may not be used to endorse or promote products
47
# derived from this software without specific prior written permission
48
#
49
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
50
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
51
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
52
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
53
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
54
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
58
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59
#
60
61
"""
62
Compressed Layered Storage
63
64
This is a storage that layers over another storage, compressing
65
objects larger than a certain threshold (default 2K).
66
67
The format of compressed pickles is an optional prefix byte followed by
68
data. If the prefix byte is 1, the data is compressed. If the
69
prefix byte is 0, the data is uncompressed. If the first byte is not 0
70
or 1, the data is assumed uncompressed. Since python pickles never
71
start with 0 or 1, they are stored directly without a prefix byte. This
72
makes compatibility with existing storages easier -- you can simply
73
stick a CompressedStorage atop an existing storage and it will work.
74
75
However, if some other layered storage sits atop this one, say an
76
encrypting storage, the "pickle" this layer receives may start with a 0
77
or 1. In that event, if this layer won't be compressing it, a 0 byte
78
will be prefixed to assure that CompressedStorage doesn't try to
79
decompress data that isn't compressed.
80
81
"""
82
83
import bz2
84
85
86
# The following would only be used for pack, and pack doesn't work...
87
class ZWrap:
88
"""
89
Wrap a function to produce a new one that can take a compressed
90
string as the first argument.
91
"""
92
93
def __init__(self, f):
94
self.func = f
95
96
def __call__(self, *args, **kw):
97
if type(args) != type(()):
98
args = tuple(args)
99
if args[0][0] == '\1':
100
args = (bz2.decompress(args[0][1:]),) + args[1:]
101
elif args[0][1] == '\0':
102
args = (args[0][1:],) + args[1:]
103
return apply(self.func, args, kw)
104
105
106
class CompressedStorage:
107
def __init__(self, base, thresh=2048):
108
self._base = base
109
self._zthresh = thresh
110
111
def __getattr__(self, attr):
112
"""
113
Pseudo-acquisition for base's stuff that we don't
114
otherwise override.
115
"""
116
return getattr(self._base, attr)
117
118
def load(self, oid, version):
119
p, serial = self._base.load(oid, version)
120
if p[0] == '\1':
121
p = bz2.decompress(p[1:])
122
elif p[0] == '\0':
123
p = p[1:]
124
return p, serial
125
126
def store(self, oid, serial, data, version, transaction):
127
if self._is_read_only:
128
raise POSException.ReadOnlyError()
129
datalen = len(data)
130
compressed = 0
131
if datalen >= self._zthresh:
132
ndata = '\1' + bz2.compress(data)
133
# print datalen - len(ndata), datalen, len(ndata)
134
if len(ndata) < datalen:
135
data = ndata
136
compressed = 1
137
138
if not compressed and data[0] in '\0\1':
139
data = '\0' + data
140
141
return self._base.store(oid, serial, data, version, transaction)
142
143
def pack(self, t, referencesf):
144
return self._base.pack(t, ZWrap(referencesf))
145
146