diff options
-rw-r--r-- | changelog.md | 4 | ||||
-rw-r--r-- | lib/pure/os.nim | 31 | ||||
-rw-r--r-- | tests/stdlib/tos.nim | 23 |
3 files changed, 49 insertions, 9 deletions
diff --git a/changelog.md b/changelog.md index 22ae9c359..5f5222e59 100644 --- a/changelog.md +++ b/changelog.md @@ -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. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 002085d97..d642e5242 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -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 diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index c053c16f2..af3606a4a 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -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: |