10000 gh-89550: Buffer GzipFile.write to reduce execution time by ~15% by CCLDArjun · Pull Request #101251 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-89550: Buffer GzipFile.write to reduce execution time by ~15% #101251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
May 8, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
initial testing to see if it's faster
  • Loading branch information
CCLDArjun committed Jan 22, 2023
commit 943ca9c92c9a897d657ea19ea7ed1e7d2f49a289
111 changes: 111 additions & 0 deletions Lib/gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import zlib
import builtins
import io
import _pyio as pyio
import _compression
import errno

__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]

Expand Down Expand Up @@ -445,6 +447,115 @@ def _read_gzip_header(fp):
_read_exact(fp, 2) # Read & discard the 16-bit header CRC
return last_mtime

class _Writable():
def writable(self):
return True

class GzipWriter(pyio.BufferedWriter):
def __init__(self, fp, compresslevel=_COMPRESS_LEVEL_BEST, buffer_size=pyio.DEFAULT_BUFFER_SIZE, mtime=None):
super().__init__(fp, buffer_size=buffer_size)

self.fileobj = fp
self.crc = zlib.crc32(b"")
self.size = 0
self.writebuf = []
self.bufsize = 0
self.offset = 0 # Current file offset for seek(), tell(), etc
self.compress = zlib.compressobj(compresslevel,
zlib.DEFLATED,
-zlib.MAX_WBITS,
zlib.DEF_MEM_LEVEL,
0)
self._write_mtime = mtime

self._write_gzip_header(compresslevel)

def close(self):
fileobj = self.fileobj
self.flush()
fileobj.write(self.compress.flush())
write32u(fileobj, self.crc)
# self.size may exceed 2 GiB, or even 4 GiB
write32u(fileobj, self.size & 0xffffffff)
self.fileobj.close()

def write(self, data):
super().write(data)

def _flush_unlocked(self):
if self.closed:
raise ValueError("flush on closed file")
while self._write_buf:
try:
#n = self.raw.write(self._write_buf)
n = self.compress_and_write(self._write_buf)
except BlockingIOError:
raise RuntimeError("self.raw should implement RawIOBase: it "
"should not raise BlockingIOError")
if n is None:
raise BlockingIOError(
errno.EAGAIN,
"write could not complete without blocking", 0)
if n > len(self._write_buf) or n < 0:
raise OSError("write() returned incorrect number of bytes")
del self._write_buf[:n]

def compress_and_write(self,data):
self._check_not_closed()
if self.fileobj is None:
raise ValueError("write() on closed GzipFile object")

if isinstance(data, (bytes, bytearray)):
length = len(data)
else:
# accept any data that supports the buffer protocol
data = memoryview(data)
length = data.nbytes

if length > 0:
self.fileobj.write(self.compress.compress(data))
self.size += length
self.crc = zlib.crc32(data, self.crc)
self.offset += length

return length

def _write_gzip_header(self, compresslevel):
self.fileobj.write(b'\037\213') # magic header
self.fileobj.write(b'\010') # compression method
try:
# RFC 1952 requires the FNAME field to be Latin-1. Do not
# include filenames that cannot be represented that way.
fname = os.path.basename(self.name)
if not isinstance(fname, bytes):
fname = fname.encode('latin-1')
if fname.endswith(b'.gz'):
fname = fname[:-3]
except UnicodeEncodeError:
fname = b''
flags = 0
if fname:
flags = FNAME
self.fileobj.write(chr(flags).encode('latin-1'))
mtime = self._write_mtime
if mtime is None:
mtime = time.time()
write32u(self.fileobj, int(mtime))
if compresslevel == _COMPRESS_LEVEL_BEST:
xfl = b'\002'
elif compresslevel == _COMPRESS_LEVEL_FAST:
xfl = b'\004'
else:
xfl = b'\000'
self.fileobj.write(xfl)
self.fileobj.write(b'\377')
if fname:
self.fileobj.write(fname + b'\000')

def _check_not_closed(self):
return self.fileobj.closed



class _GzipReader(_compression.DecompressReader):
def __init__(self, fp):
Expand Down
0