2.5x- 3x faster copyFile on osx (#16883)
This commit is contained in:
parent
fa9dc31899
commit
917f12ae52
3 changed files with 49 additions and 9 deletions
|
|
@ -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.
|
- `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
|
## Compiler changes
|
||||||
|
|
||||||
- Added `--declaredlocs` to show symbol declaration location in messages.
|
- Added `--declaredlocs` to show symbol declaration location in messages.
|
||||||
|
|
|
||||||
|
|
@ -1634,6 +1634,24 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
|
||||||
var res2 = setFileAttributesA(filename, res)
|
var res2 = setFileAttributesA(filename, res)
|
||||||
if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
|
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",
|
proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
|
tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
|
||||||
## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
|
## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
|
||||||
|
|
@ -1654,6 +1672,9 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
## If `dest` already exists, the file attributes
|
## If `dest` already exists, the file attributes
|
||||||
## will be preserved and the content overwritten.
|
## 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:
|
## See also:
|
||||||
## * `copyDir proc <#copyDir,string,string>`_
|
## * `copyDir proc <#copyDir,string,string>`_
|
||||||
## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
|
## * `copyFileWithPermissions proc <#copyFileWithPermissions,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))
|
if copyFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError(), $(source, dest))
|
||||||
else:
|
else:
|
||||||
if copyFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError(), $(source, dest))
|
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:
|
else:
|
||||||
# generic version of copyFile which works for any platform:
|
# generic version of copyFile which works for any platform:
|
||||||
const bufSize = 8000 # better for memory manager
|
const bufSize = 8000 # better for memory manager
|
||||||
|
|
|
||||||
|
|
@ -40,20 +40,25 @@ block fileOperations:
|
||||||
doAssertRaises(OSError): copyFile(dname/"nonexistant.txt", dname/"nonexistant.txt")
|
doAssertRaises(OSError): copyFile(dname/"nonexistant.txt", dname/"nonexistant.txt")
|
||||||
let fname = "D20201009T112235"
|
let fname = "D20201009T112235"
|
||||||
let fname2 = "D20201009T112235.2"
|
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"
|
let sub = "sub"
|
||||||
doAssertRaises(OSError): copyFile(dname/fname, dname/sub/fname2)
|
doAssertRaises(OSError): copyFile(file, dname/sub/fname2)
|
||||||
doAssertRaises(OSError): copyFileToDir(dname/fname, dname/sub)
|
doAssertRaises(OSError): copyFileToDir(file, dname/sub)
|
||||||
doAssertRaises(ValueError): copyFileToDir(dname/fname, "")
|
doAssertRaises(ValueError): copyFileToDir(file, "")
|
||||||
copyFile(dname/fname, dname/fname2)
|
copyFile(file, file2)
|
||||||
doAssert fileExists(dname/fname2)
|
doAssert fileExists(file2)
|
||||||
|
doAssert readFile(file2) == str
|
||||||
createDir(dname/sub)
|
createDir(dname/sub)
|
||||||
copyFileToDir(dname/fname, dname/sub)
|
copyFileToDir(file, dname/sub)
|
||||||
doAssert fileExists(dname/sub/fname)
|
doAssert fileExists(dname/sub/fname)
|
||||||
removeDir(dname/sub)
|
removeDir(dname/sub)
|
||||||
doAssert not dirExists(dname/sub)
|
doAssert not dirExists(dname/sub)
|
||||||
removeFile(dname/fname)
|
removeFile(file)
|
||||||
removeFile(dname/fname2)
|
removeFile(file2)
|
||||||
|
|
||||||
# Test creating files and dirs
|
# Test creating files and dirs
|
||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue