2.5x- 3x faster copyFile on osx (#16883)

This commit is contained in:
Timothee Cour
2021-02-01 17:38:17 -08:00
committed by GitHub
parent fa9dc31899
commit 917f12ae52
3 changed files with 49 additions and 9 deletions

View File

@@ -121,6 +121,10 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.
- `typetraits.distinctBase` now is identity instead of error for non distinct types.
- `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`;
use `-d:nimLegacyCopyFile` for OSX < 10.5.
## Compiler changes
- Added `--declaredlocs` to show symbol declaration location in messages.

View File

@@ -1634,6 +1634,24 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
var res2 = setFileAttributesA(filename, res)
if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
const hasCCopyfile = defined(osx) and not defined(nimLegacyCopyFile)
# xxx instead of `nimLegacyCopyFile`, support something like: `when osxVersion >= (10, 5)`
when hasCCopyfile:
# `copyfile` API available since osx 10.5.
{.push nodecl, header: "<copyfile.h>".}
type
copyfile_state_t {.nodecl.} = pointer
copyfile_flags_t = cint
proc copyfile_state_alloc(): copyfile_state_t
proc copyfile_state_free(state: copyfile_state_t): cint
proc c_copyfile(src, dst: cstring, state: copyfile_state_t, flags: copyfile_flags_t): cint {.importc: "copyfile".}
# replace with `let` pending bootstrap >= 1.4.0
var
COPYFILE_DATA {.nodecl.}: copyfile_flags_t
COPYFILE_XATTR {.nodecl.}: copyfile_flags_t
{.pop.}
proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
@@ -1653,6 +1671,9 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
##
## If `dest` already exists, the file attributes
## will be preserved and the content overwritten.
##
## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless
## `-d:nimLegacyCopyFile` is used.
##
## See also:
## * `copyDir proc <#copyDir,string,string>`_
@@ -1668,6 +1689,16 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
if copyFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError(), $(source, dest))
else:
if copyFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError(), $(source, dest))
elif hasCCopyfile:
let state = copyfile_state_alloc()
# xxx `COPYFILE_STAT` could be used for one-shot `copyFileWithPermissions`.
let status = c_copyfile(source.cstring, dest.cstring, state, COPYFILE_DATA)
if status != 0:
let err = osLastError()
discard copyfile_state_free(state)
raiseOSError(err, $(source, dest))
let status2 = copyfile_state_free(state)
if status2 != 0: raiseOSError(osLastError(), $(source, dest))
else:
# generic version of copyFile which works for any platform:
const bufSize = 8000 # better for memory manager

View File

@@ -40,20 +40,25 @@ block fileOperations:
doAssertRaises(OSError): copyFile(dname/"nonexistant.txt", dname/"nonexistant.txt")
let fname = "D20201009T112235"
let fname2 = "D20201009T112235.2"
writeFile(dname/fname, "foo")
let str = "foo1\0foo2\nfoo3\0"
let file = dname/fname
let file2 = dname/fname2
writeFile(file, str)
doAssert readFile(file) == str
let sub = "sub"
doAssertRaises(OSError): copyFile(dname/fname, dname/sub/fname2)
doAssertRaises(OSError): copyFileToDir(dname/fname, dname/sub)
doAssertRaises(ValueError): copyFileToDir(dname/fname, "")
copyFile(dname/fname, dname/fname2)
doAssert fileExists(dname/fname2)
doAssertRaises(OSError): copyFile(file, dname/sub/fname2)
doAssertRaises(OSError): copyFileToDir(file, dname/sub)
doAssertRaises(ValueError): copyFileToDir(file, "")
copyFile(file, file2)
doAssert fileExists(file2)
doAssert readFile(file2) == str
createDir(dname/sub)
copyFileToDir(dname/fname, dname/sub)
copyFileToDir(file, dname/sub)
doAssert fileExists(dname/sub/fname)
removeDir(dname/sub)
doAssert not dirExists(dname/sub)
removeFile(dname/fname)
removeFile(dname/fname2)
removeFile(file)
removeFile(file2)
# Test creating files and dirs
for dir in dirs: