├── .gitignore ├── LICENSE ├── README.rst ├── fcp.nim ├── fcp.nim.cfg └── fcp.nimble /.gitignore: -------------------------------------------------------------------------------- 1 | fcp 2 | fcp.exe 3 | nimcache/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ryan Gonzalez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | fcp 2 | === 3 | 4 | An ultra-fast, multi-threaded file-copy utility written in `Nim `_. fcp excels in moving directories with a large number of moderately-sized or large files. 5 | 6 | Building 7 | ******** 8 | 9 | If you have `Nimble `_ installed:: 10 | 11 | $ nimble install 12 | 13 | Otherwise:: 14 | 15 | $ nim c fcp 16 | 17 | Usage 18 | ***** 19 | 20 | :: 21 | 22 | fcp [] 23 | 24 | TODO 25 | **** 26 | 27 | - Test on Windows. 28 | -------------------------------------------------------------------------------- /fcp.nim: -------------------------------------------------------------------------------- 1 | import os, threadpool, strutils 2 | 3 | type 4 | FilePath = object of RootObj 5 | src, dst: string 6 | FileList = seq[FilePath] 7 | 8 | var 9 | live = 0 10 | copied = 0 11 | threadMax = 20 12 | 13 | when declared(atomicAddFetch): 14 | template ainc(a: int) = discard atomicAddFetch(addr(a), 1, ATOMIC_RELAXED) 15 | template adec(a: int) = discard atomicAddFetch(addr(a), -1, ATOMIC_RELAXED) 16 | else: 17 | template ainc(a: int) = discard addAndFetch(addr(a), 1) 18 | template adec(a: int) = discard addAndFetch(addr(a), -1) 19 | 20 | proc `$$`[T](s: T, z: T): string = align($s, len($z), '0') 21 | 22 | proc copy(file: FilePath) {.thread.} = 23 | copyFileWithPermissions file.src, file.dst 24 | ainc copied 25 | adec live 26 | 27 | proc copyFiles(files: FileList) = 28 | var total = files.len 29 | 30 | template report() = 31 | stdout.flushFile 32 | stdout.write "\rFiles copied: $#, files left: $#, active threads: $#" % [ 33 | copied $$ total, 34 | (total-copied) $$ total, 35 | live $$ threadMax 36 | ] 37 | stdout.flushFile 38 | 39 | for file in files: 40 | while live+1 > threadMax: 41 | stdout.flushFile 42 | ainc live 43 | spawn copy(file) 44 | report() 45 | while total-copied != 0: report() 46 | report() 47 | sync() 48 | report() 49 | echo() 50 | 51 | proc setupThreadCount(threads: string) = 52 | threadMax = threads.parseInt 53 | if threadMax <= 0: 54 | quit "Cannot have 0 or less threads" 55 | 56 | proc setupFileList(src, dst: string): seq[FilePath] = 57 | result = @[] 58 | if not existsDir src: 59 | quit "Source either doesn't exist or isn't a directory" 60 | 61 | if existsFile dst: 62 | quit "Destination is a file" 63 | 64 | createDir dst 65 | 66 | echo "Building directory list..." 67 | 68 | for path in walkDirRec src: 69 | var 70 | dstfile = dst / path[src.len .. ^1] 71 | parent = dstfile.parentDir 72 | if parent != "": createDir parent 73 | result.add FilePath(src: path, dst: dstfile) 74 | 75 | proc main() = 76 | var 77 | src, dst: string 78 | args: seq[string] = commandLineParams() 79 | 80 | if args.len != 2 and args.len != 3: 81 | quit "usage: fcp [=$#]" % [$threadMax] 82 | 83 | src = args[0] 84 | dst = args[1] 85 | 86 | if args.len == 3: setupThreadCount args[2] 87 | 88 | copyFiles setupFileList(src, dst) 89 | 90 | when isMainModule: main() 91 | -------------------------------------------------------------------------------- /fcp.nim.cfg: -------------------------------------------------------------------------------- 1 | threads:on 2 | -d:release 3 | -------------------------------------------------------------------------------- /fcp.nimble: -------------------------------------------------------------------------------- 1 | [Package] 2 | name = "fcp" 3 | version = "0.1.0" 4 | author = "Ryan Gonzalez" 5 | description = "A fast, multi-threaded file copy utility" 6 | license = "MIT" 7 | bin = "fcp" 8 | InstallFiles = """ 9 | fcp.nim 10 | fcp.nim.cfg 11 | """ 12 | 13 | [Deps] 14 | requires = "nim >= 0.10.3" 15 | --------------------------------------------------------------------------------