Skip to content

Commit

Permalink
Use higher time resolution when available in os.nim
Browse files Browse the repository at this point in the history
  • Loading branch information
GULPF committed May 3, 2018
1 parent 5063437 commit 65bb390
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 12 deletions.
41 changes: 29 additions & 12 deletions lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ when defined(windows):
import winlean
elif defined(posix):
import posix

proc toTime(ts: Timespec): times.Time {.inline.} =
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)

# Some systems have Stat.st_*time (second resolution) instead of
# Stat.st_*tim (nanosecond resolution).
const StatHasNano = not defined(macosx) and not defined(android)

when not StatHasNano:
proc st_atim*(s: Stat): TimeSpec {.inline.} = result.tv_sec = s.st_atime
proc st_mtim*(s: Stat): TimeSpec {.inline.} = result.tv_sec = s.st_mtime
proc st_ctim*(s: Stat): TimeSpec {.inline.} = result.tv_sec = s.st_ctime

else:
{.error: "OS module not ported to your operating system!".}

Expand Down Expand Up @@ -183,7 +196,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".}
when defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_mtime.int64)
result = res.st_mtim.toTime
else:
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
Expand All @@ -196,7 +209,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
when defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_atime.int64)
result = res.st_atim.toTime
else:
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
Expand All @@ -213,7 +226,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
when defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_ctime.int64)
result = res.st_ctim.toTime
else:
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
Expand All @@ -225,10 +238,13 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
## modification time is later than `b`'s.
when defined(posix):
result = getLastModificationTime(a) - getLastModificationTime(b) >= DurationZero
# Posix's resolution sucks so, we use '>=' for posix.
# If we don't have access to nanosecond resolution, use '>='
when not StatHasNano:
result = getLastModificationTime(a) >= getLastModificationTime(b)
else:
result = getLastModificationTime(a) > getLastModificationTime(b)
else:
result = getLastModificationTime(a) - getLastModificationTime(b) > DurationZero
result = getLastModificationTime(a) > getLastModificationTime(b)

proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
## Returns the `current working directory`:idx:.
Expand Down Expand Up @@ -1507,7 +1523,7 @@ type

template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
## Transforms the native file info structure into the one nim uses.
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows,
## or a 'Stat' structure on posix
when defined(Windows):
template merge(a, b): untyped = a or (b shl 32)
Expand All @@ -1533,17 +1549,16 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
formalInfo.kind = succ(result.kind)


else:
template checkAndIncludeMode(rawMode, formalMode: untyped) =
if (rawInfo.st_mode and rawMode) != 0'i32:
formalInfo.permissions.incl(formalMode)
formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
formalInfo.size = rawInfo.st_size
formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt
formalInfo.lastAccessTime = fromUnix(rawInfo.st_atime.int64)
formalInfo.lastWriteTime = fromUnix(rawInfo.st_mtime.int64)
formalInfo.creationTime = fromUnix(rawInfo.st_ctime.int64)
formalInfo.lastAccessTime = rawInfo.st_atim.toTime
formalInfo.lastWriteTime = rawInfo.st_mtim.toTime
formalInfo.creationTime = rawInfo.st_ctim.toTime

result.permissions = {}
checkAndIncludeMode(S_IRUSR, fpUserRead)
Expand Down Expand Up @@ -1657,7 +1672,9 @@ proc setLastModificationTime*(file: string, t: times.Time) =
## an error.
when defined(posix):
let unixt = posix.Time(t.toUnix)
var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification]
let micro = convert(Nanoseconds, Microseconds, t.nanosecond)
var timevals = [Timeval(tv_sec: unixt, tv_usec: micro),
Timeval(tv_sec: unixt, tv_usec: micro)] # [last access, last modification]
if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
else:
let h = openHandle(path = file, writeAccess = true)
Expand Down
10 changes: 10 additions & 0 deletions tests/stdlib/tos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Raises
true
true
true
true
'''
"""
# test os path creation, iteration, and deletion
Expand Down Expand Up @@ -129,3 +130,12 @@ echo fileExists("../dest/a/b/file.txt")

echo fileExists("../dest/a/b/c/fileC.txt")
removeDir("../dest")

# Test get/set modification times
# Should support at least microsecond resolution
import times
let tm = fromUnix(0) + 100.microseconds
writeFile("a", "")
setLastModificationTime("a", tm)
echo getLastModificationTime("a") == tm
removeFile("a")

0 comments on commit 65bb390

Please sign in to comment.