-
Notifications
You must be signed in to change notification settings - Fork 4
/
quicklz.py
executable file
·132 lines (105 loc) · 4.13 KB
/
quicklz.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""
This is a I{fast} compression library which uses quicklz to compress and
decompress data. Some ideas and code comes from the Python 2.7 gzip.py module.
The user of this class doesn't have to worry about compression,
but random access is not allowed.
QuickLZ is hosted at U{http://www.quicklz.com}
"""
from __future__ import absolute_import
class QuickLZFile(io.BufferedIOBase):
"""
The QuickLZFile class simulates most of the methods of a file object
with the exception of the readinto() and truncate() methods.
"""
myfileobj = None
max_read_chunk = 10 * 1024 * 1024 # 10Mb
def __init__(self, filename=None, mode=None,
compresslevel=2, fileobj=None, mtime=None):
"""
QuickLZFile Constructor.
"""
# guarantee the file is opened in binary mode on platforms
# that care about that sort of thing
if mode and 'b' not in mode:
mode += 'b'
if fileobj is None:
fileobj = self.myfileobj = __builtin__.open(filename, mode or 'rb')
if filename is None:
if hasattr(fileobj, 'name'): filename = fileobj.name
else: filename = ''
if mode is None:
if hasattr(fileobj, 'mode'): mode = fileobj.mode
else: mode = 'rb'
self.name = filename
if mode[0:1] == 'r':
self.mode = READ
# Buffer data read from quicklz file. extrastart is offset in
# stream where buffer starts. extrasize is number of
# bytes remaining in buffer from current stream position.
self.readbuf = ""
self.readbufsize = 0
# Starts small, scales exponentially
self.min_readsize = 100
elif mode[0:1] == 'w' or mode[0:1] == 'a':
self.mode = WRITE
self._init_write(filename)
else:
raise IOError, "Mode " + mode + " not supported"
self.fileobj = fileobj
self.offset = 0
self.mtime = mtime
if self.mode == WRITE:
self._write_qzip_header()
def _init_write(self, filename):
self.size = 0
self.writebuf = []
self.bufsize = 0
def _write_qzip_header(self):
# no-op
pass
def _init_read():
self.size = 0
def write(self, data):
if self.mode != WRITE:
import errno
raise IOError(errno.EBADF, "write() on read-only QuickLZFile object")
if self.fileobj is None:
raise ValueError, "write() on closed QuickLZFile object"
if hasattr(data, "tobytes"):
assert isinstance(data, memoryview)
data = data.tobytes()
if len(data) > 0:
self.size = self.size + len(data)
self.fileobj.write( self.compress.compress(data) )
self.offset += len(data)
return len(data)
def read(self, size=-1):
if self.mode != READ:
import errno
raise IOError(errno.EBADF, "read() on write-only QuickLZFile object")
if self.readbufsize <= 0 and self.fileobj is None:
raise EOFError, "Reached EOF"
readsize = 1024
if size < 0: # get the whole thing
try:
while True:
state = qlz_state_decompress()
header_buffer = self.fileobj.read(9)
size = qlz_size_compressed(header_buffer)
readbuffer = self.fileobj.read(size - 9)
self.readbuffer += qlz_decompress(readbuffer, state)
except EOFError:
size = self.extrasize
else: # just get some more of it
try:
while size > self.extrasize: #XXX: TODO: edit here
self._read(readsize)
readsize = min(self.max_read_chunk, readsize * 2)
except EOFError:
if size > self.extrasize:
size = self.extrasize
offset = self.offset - self.extrastart
chunk = self.extrabuf[offset: offset + size]
self.extrasize = self.extrasize - size
self.offset += size
return chunk