Skip to content

Commit 06e388a

Browse files
author
Kazuki Suzuki Przyborowski
committed
Update pyfoxfile.py
1 parent 123d420 commit 06e388a

1 file changed

Lines changed: 105 additions & 5 deletions

File tree

pyfoxfile.py

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4983,7 +4983,7 @@ def AppendFilesWithContent(infiles, fp, dirlistfromtxt=False, filevalues=[], ext
49834983
curcompression = "none"
49844984
if not followlink and ftype in data_types:
49854985
with open(fname, "rb") as fpc:
4986-
shutil.copyfileobj(fpc, fcontents)
4986+
copy_opaque(fpc, fcontents, bufsize=1 << 20) # 1 MiB chunks, opaque copy
49874987
typechecktest = CheckCompressionType(fcontents, filestart=0, closefp=False)
49884988
fcontents.seek(0, 0)
49894989
fcencoding = GetFileEncoding(fcontents, 0, False)
@@ -5030,7 +5030,7 @@ def AppendFilesWithContent(infiles, fp, dirlistfromtxt=False, filevalues=[], ext
50305030
return False
50315031
flstatinfo = os.stat(flinkname)
50325032
with open(flinkname, "rb") as fpc:
5033-
shutil.copyfileobj(fpc, fcontents)
5033+
copy_opaque(fpc, fcontents, bufsize=1 << 20) # 1 MiB chunks, opaque copy
50345034
typechecktest = CheckCompressionType(fcontents, filestart=0, closefp=False)
50355035
fcontents.seek(0, 0)
50365036
fcencoding = GetFileEncoding(fcontents, 0, False)
@@ -6330,6 +6330,106 @@ def open_adapter(obj_or_path, mode="rb", use_mmap=False, mmap_size=None):
63306330

63316331
# Assumes you already have: compressionsupport, outextlistwd, MkTempFile, etc.
63326332

6333+
def ensure_filelike(infile, mode="rb", use_mmap=False):
6334+
"""
6335+
Accepts either a path or an existing file-like object.
6336+
Always returns a FileLikeAdapter (optionally mmap-backed).
6337+
"""
6338+
if hasattr(infile, "read") or hasattr(infile, "write"):
6339+
# Already a file-like
6340+
fp = infile
6341+
else:
6342+
try:
6343+
fp = open(infile, mode)
6344+
except IOError: # covers FileNotFoundError on Py2
6345+
return False
6346+
6347+
# Wrap in FileLikeAdapter for consistent interface
6348+
return open_adapter(fp, mode=mode, use_mmap=use_mmap)
6349+
6350+
def fast_copy(infp, outfp, bufsize=1 << 20):
6351+
buf = bytearray(bufsize)
6352+
mv = memoryview(buf)
6353+
while True:
6354+
n = getattr(infp, "readinto", None)
6355+
if callable(n):
6356+
n = infp.readinto(mv)
6357+
if not n:
6358+
break
6359+
outfp.write(mv[:n])
6360+
else:
6361+
# Fallback if readinto is missing
6362+
data = infp.read(bufsize)
6363+
if not data:
6364+
break
6365+
outfp.write(data)
6366+
6367+
def copy_file_to_mmap_dest(src_path, outfp, chunk_size=8 << 20):
6368+
with open(src_path, "rb") as fp:
6369+
try:
6370+
mm = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
6371+
pos, size = 0, len(mm)
6372+
while pos < size:
6373+
end = min(pos + chunk_size, size)
6374+
outfp.write(mm[pos:end]) # outfp is your mmap-backed FileLikeAdapter
6375+
pos = end
6376+
mm.close()
6377+
except (ValueError, mmap.error, OSError):
6378+
# fall back
6379+
shutil.copyfileobj(fp, outfp, length=chunk_size)
6380+
6381+
def copy_opaque(src, dst, bufsize=1 << 20, grow_step=64 << 20):
6382+
"""
6383+
Copy opaque bytes from 'src' (any readable file-like) to 'dst'
6384+
(your mmap-backed FileLikeAdapter or any writable file-like).
6385+
- Uses readinto when available (zero extra allocations).
6386+
- If dst is mmapped and size is exceeded, auto-grow via truncate().
6387+
Returns total bytes copied.
6388+
"""
6389+
total = 0
6390+
buf = bytearray(bufsize)
6391+
mv = memoryview(buf)
6392+
6393+
# Best-effort: if src supports seek/tell, start from current position
6394+
# and do not disturb caller beyond what we read.
6395+
while True:
6396+
# Prefer readinto to avoid extra allocations
6397+
readinto = getattr(src, "readinto", None)
6398+
if callable(readinto):
6399+
n = src.readinto(mv)
6400+
if not n:
6401+
break
6402+
# write; if mmap too small, grow and retry once
6403+
try:
6404+
dst.write(mv[:n])
6405+
except IOError:
6406+
# likely "write past mapped size"; try to grow
6407+
try:
6408+
new_size = max(dst.tell() + n, dst.tell() + grow_step)
6409+
dst.truncate(new_size)
6410+
dst.write(mv[:n])
6411+
except Exception:
6412+
raise
6413+
total += n
6414+
else:
6415+
chunk = src.read(bufsize)
6416+
if not chunk:
6417+
break
6418+
try:
6419+
dst.write(chunk)
6420+
except IOError:
6421+
try:
6422+
new_size = max(dst.tell() + len(chunk), dst.tell() + grow_step)
6423+
dst.truncate(new_size)
6424+
dst.write(chunk)
6425+
except Exception:
6426+
raise
6427+
total += len(chunk)
6428+
6429+
# Your adapter's flush() already does mm.flush() + fp.flush() + fsync(fd) when possible
6430+
dst.flush()
6431+
return total
6432+
63336433
def CompressOpenFileAlt(fp, compression="auto", compressionlevel=None,
63346434
compressionuselist=compressionlistalt,
63356435
formatspecs=__file_format_dict__):
@@ -6795,7 +6895,7 @@ def PackFoxFile(infiles, outfile, dirlistfromtxt=False, fmttype="auto", compress
67956895
curcompression = "none"
67966896
if not followlink and ftype in data_types:
67976897
with open(fname, "rb") as fpc:
6798-
shutil.copyfileobj(fpc, fcontents)
6898+
copy_opaque(fpc, fcontents, bufsize=1 << 20) # 1 MiB chunks, opaque copy
67996899
typechecktest = CheckCompressionType(fcontents, filestart=0, closefp=False)
68006900
fcontents.seek(0, 0)
68016901
fcencoding = GetFileEncoding(fcontents, 0, False)
@@ -6842,7 +6942,7 @@ def PackFoxFile(infiles, outfile, dirlistfromtxt=False, fmttype="auto", compress
68426942
return False
68436943
flstatinfo = os.stat(flinkname)
68446944
with open(flinkname, "rb") as fpc:
6845-
shutil.copyfileobj(fpc, fcontents)
6945+
copy_opaque(fpc, fcontents, bufsize=1 << 20) # 1 MiB chunks, opaque copy
68466946
typechecktest = CheckCompressionType(fcontents, filestart=0, closefp=False)
68476947
fcontents.seek(0, 0)
68486948
fcencoding = GetFileEncoding(fcontents, 0, False)
@@ -7136,7 +7236,7 @@ def PackFoxFileFromTarFile(infile, outfile, fmttype="auto", compression="auto",
71367236
curcompression = "none"
71377237
if ftype in data_types:
71387238
fpc = tarfp.extractfile(member)
7139-
shutil.copyfileobj(fpc, fcontents)
7239+
copy_opaque(fpc, fcontents, bufsize=1 << 20) # 1 MiB chunks, opaque copy
71407240
fpc.close()
71417241
typechecktest = CheckCompressionType(fcontents, filestart=0, closefp=False)
71427242
fcontents.seek(0, 0)

0 commit comments

Comments
 (0)