diff options
Diffstat (limited to 'lib/std')
60 files changed, 777 insertions, 650 deletions
diff --git a/lib/std/appdirs.nim b/lib/std/appdirs.nim index e648fe0c1..963451efe 100644 --- a/lib/std/appdirs.nim +++ b/lib/std/appdirs.nim @@ -1,5 +1,7 @@ ## This module implements helpers for determining special directories used by apps. +## .. importdoc:: paths.nim + from std/private/osappdirs import nil import std/paths import std/envvars @@ -15,6 +17,23 @@ proc getHomeDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} = ## * `getTempDir proc`_ result = Path(osappdirs.getHomeDir()) +proc getDataDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the data directory of the current user for applications. + ## + ## On non-Windows OSs, this proc conforms to the XDG Base Directory + ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment + ## variable if it is set, otherwise it returns the default configuration + ## directory ("~/.local/share" or "~/Library/Application Support" on macOS). + ## + ## See also: + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `expandTilde proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ + result = Path(osappdirs.getDataDir()) + proc getConfigDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. ## diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 03bab1b1b..56c37d205 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -7,6 +7,10 @@ # distribution, for details about the copyright. # +when not defined(nimPreviewSlimSystem) and not declared(sysFatal): + include "system/rawquits" + include "system/fatal" + ## This module implements assertion handling. import std/private/miscdollars @@ -23,22 +27,18 @@ proc `$`(info: InstantiationInfo): string = # --------------------------------------------------------------------------- -when not defined(nimHasSinkInference): - {.pragma: nosinks.} proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} = ## Raises an `AssertionDefect` with `msg`. - raise newException(AssertionDefect, msg) + when defined(nimPreviewSlimSystem): + raise newException(AssertionDefect, msg) + else: + sysFatal(AssertionDefect, msg) proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = ## Raises an `AssertionDefect` with `msg`, but this is hidden ## from the effect system. Called when an assertion failed. - # trick the compiler to not list `AssertionDefect` when called - # by `assert`. - # xxx simplify this pending bootstrap >= 1.4.0, after which cast not needed - # anymore since `Defect` can't be raised. - type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, tags: [].} - cast[Hide](raiseAssert)(msg) + raiseAssert(msg) template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) = when enabled: @@ -98,6 +98,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) = const begin = "expected raising '" & astToStr(exception) & "', instead" const msgEnd = " by: " & astToStr(code) template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd) + {.push warning[BareExcept]:off.} when Exception is exception: try: if true: @@ -116,5 +117,6 @@ template doAssertRaises*(exception: typedesc, code: untyped) = mixin `$` # alternatively, we could define $cstring in this module raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) except: raisedForeign() + {.pop.} if wrong: raiseAssert(begin & " nothing was raised" & msgEnd) diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim index 3208da1b8..0ba4619e5 100644 --- a/lib/std/cmdline.nim +++ b/lib/std/cmdline.nim @@ -30,9 +30,9 @@ const weirdTarget = defined(nimscript) or defined(js) when weirdTarget: discard elif defined(windows): - import winlean + import std/winlean elif defined(posix): - import posix + import std/posix else: {.error: "The cmdline module has not been implemented for the target platform.".} @@ -40,10 +40,7 @@ else: # Needed by windows in order to obtain the command line for targets # other than command line targets when defined(windows) and not weirdTarget: - when useWinUnicode: - template getCommandLine*(): untyped = getCommandLineW() - else: - template getCommandLine*(): untyped = getCommandLineA() + template getCommandLine*(): untyped = getCommandLineW() proc parseCmdLine*(c: string): seq[string] {. @@ -141,7 +138,7 @@ proc parseCmdLine*(c: string): seq[string] {. while i < c.len and c[i] > ' ': add(a, c[i]) inc(i) - add(result, a) + add(result, move a) when defined(nimdoc): # Common forward declaration docstring block for parameter retrieval procs. @@ -166,11 +163,12 @@ when defined(nimdoc): ## ## **Examples:** ## - ## .. code-block:: nim + ## ```nim ## when declared(paramCount): ## # Use paramCount() here ## else: ## # Do something else! + ## ``` proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = ## Returns the `i`-th `command line argument`:idx: given to the application. @@ -183,7 +181,7 @@ when defined(nimdoc): ## Similarly to `argv`:idx: in C, ## it is possible to call `paramStr(0)` but this will return OS specific ## contents (usually the name of the invoked executable). You should avoid - ## this and call `getAppFilename()`_ instead. + ## this and call `getAppFilename() <os.html#getAppFilename>`_ instead. ## ## **Availability**: When generating a dynamic library (see `--app:lib`) on ## Posix this proc is not defined. @@ -194,15 +192,16 @@ when defined(nimdoc): ## * `parseCmdLine proc`_ ## * `paramCount proc`_ ## * `commandLineParams proc`_ - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <os.html#getAppFilename>`_ ## ## **Examples:** ## - ## .. code-block:: nim + ## ```nim ## when declared(paramStr): ## # Use paramStr() here ## else: ## # Do something else! + ## ``` elif defined(nimscript): discard elif defined(nodejs): @@ -283,7 +282,7 @@ when declared(paramCount) or defined(nimdoc): ## Convenience proc which returns the command line parameters. ## ## This returns **only** the parameters. If you want to get the application - ## executable filename, call `getAppFilename()`_. + ## executable filename, call `getAppFilename() <os.html#getAppFilename>`_. ## ## **Availability**: On Posix there is no portable way to get the command ## line from a DLL and thus the proc isn't defined in this environment. You @@ -295,15 +294,16 @@ when declared(paramCount) or defined(nimdoc): ## * `parseCmdLine proc`_ ## * `paramCount proc`_ ## * `paramStr proc`_ - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <os.html#getAppFilename>`_ ## ## **Examples:** ## - ## .. code-block:: nim + ## ```nim ## when declared(commandLineParams): ## # Use commandLineParams() here ## else: ## # Do something else! + ## ``` result = @[] for i in 1..paramCount(): result.add(paramStr(i)) diff --git a/lib/std/decls.nim b/lib/std/decls.nim index 3f774cd08..bb7ec3593 100644 --- a/lib/std/decls.nim +++ b/lib/std/decls.nim @@ -1,6 +1,6 @@ ## This module implements syntax sugar for some declarations. -import macros +import std/macros macro byaddr*(sect) = ## Allows a syntax for l-value references, being an exact analog to diff --git a/lib/std/dirs.nim b/lib/std/dirs.nim index df6107c51..380d6d08f 100644 --- a/lib/std/dirs.nim +++ b/lib/std/dirs.nim @@ -1,6 +1,6 @@ ## This module implements directory handling. -from paths import Path, ReadDirEffect, WriteDirEffect +from std/paths import Path, ReadDirEffect, WriteDirEffect from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir, moveDir, walkDir, setCurrentDir, @@ -8,7 +8,7 @@ from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDi export PathComponent -proc dirExists*(dir: Path): bool {.inline, tags: [ReadDirEffect].} = +proc dirExists*(dir: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} = ## Returns true if the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. result = dirExists(dir.string) diff --git a/lib/std/editdistance.nim b/lib/std/editdistance.nim index 9f29c5c05..40c0017ae 100644 --- a/lib/std/editdistance.nim +++ b/lib/std/editdistance.nim @@ -10,7 +10,7 @@ ## This module implements an algorithm to compute the ## `edit distance`:idx: between two Unicode strings. -import unicode +import std/unicode proc editDistance*(a, b: string): int {.noSideEffect.} = ## Returns the **unicode-rune** edit distance between `a` and `b`. @@ -18,7 +18,7 @@ proc editDistance*(a, b: string): int {.noSideEffect.} = ## This uses the `Levenshtein`:idx: distance algorithm with only a linear ## memory overhead. runnableExamples: static: doAssert editdistance("Kitten", "Bitten") == 1 - if len(a) > len(b): + if runeLen(a) > runeLen(b): # make `b` the longer string return editDistance(b, a) # strip common prefix diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index fb057a669..3d1b4ffd3 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -14,7 +14,7 @@ ## One can test for the existence of this standard module ## via `defined(nimHasEffectTraitsModule)`. -import macros +import std/macros proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim index a8f0e1ba7..beb65ed30 100644 --- a/lib/std/enumerate.nim +++ b/lib/std/enumerate.nim @@ -11,7 +11,7 @@ ## macro system. import std/private/since -import macros +import std/macros macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = @@ -21,7 +21,7 @@ macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = ## The default starting count `0` can be manually overridden if needed. runnableExamples: let a = [10, 20, 30] - var b: seq[(int, int)] + var b: seq[(int, int)] = @[] for i, x in enumerate(a): b.add((i, x)) assert b == @[(0, 10), (1, 20), (2, 30)] diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 9d4ff1bcf..9c338817d 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -import macros -from typetraits import OrdinalEnum, HoleyEnum +import std/macros +from std/typetraits import OrdinalEnum, HoleyEnum when defined(nimPreviewSlimSystem): import std/assertions @@ -21,32 +21,34 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, # Generates a case stmt, which assigns the correct enum field given # a normalized string comparison to the `argSym` input. # string normalization is done using passed normalizer. - # NOTE: for an enum with fields Foo, Bar, ... we cannot generate - # `of "Foo".nimIdentNormalize: Foo`. - # This will fail, if the enum is not defined at top level (e.g. in a block). - # Thus we check for the field value of the (possible holed enum) and convert - # the integer value to the generic argument `typ`. let typ = typ.getTypeInst[1] - let impl = typ.getImpl[2] + let typSym = typ.getTypeImpl.getTypeInst # skip aliases etc to get type sym + let impl = typSym.getImpl[2] expectKind impl, nnkEnumTy let normalizerNode = quote: `normalizer` expectKind normalizerNode, nnkSym result = nnkCaseStmt.newTree(newCall(normalizerNode, argSym)) # stores all processed field strings to give error msg for ambiguous enums var foundFields: seq[string] = @[] + var fVal = "" var fStr = "" # string of current field var fNum = BiggestInt(0) # int value of current field for f in impl: case f.kind of nnkEmpty: continue # skip first node of `enumTy` - of nnkSym, nnkIdent: fStr = f.strVal + of nnkSym, nnkIdent: + fVal = f.strVal + fStr = fVal of nnkAccQuoted: - fStr = "" + fVal = "" for ch in f: - fStr.add ch.strVal + fVal.add ch.strVal + fStr = fVal of nnkEnumFieldDef: + fVal = f[0].strVal case f[1].kind - of nnkStrLit: fStr = f[1].strVal + of nnkStrLit: + fStr = f[1].strVal of nnkTupleConstr: fStr = f[1][1].strVal fNum = f[1][0].intVal @@ -64,7 +66,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, if fNum >= userMin and fNum <= userMax: fStr = normalizer(fStr) if fStr notin foundFields: - result.add nnkOfBranch.newTree(newLit fStr, nnkCall.newTree(typ, newLit fNum)) + result.add nnkOfBranch.newTree(newLit fStr, newDotExpr(typ, ident fVal)) foundFields.add fStr else: error("Ambiguous enums cannot be parsed, field " & $fStr & @@ -80,7 +82,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, result.add nnkElse.newTree(default) macro enumFullRange(a: typed): untyped = - newNimNode(nnkCurly).add(a.getType[1][1..^1]) + newNimNode(nnkBracket).add(a.getType[1][1..^1]) macro enumNames(a: typed): untyped = # this could be exported too; in particular this could be useful for enum with holes. @@ -112,9 +114,9 @@ const invalidSlot = uint8.high proc genLookup[T: typedesc[HoleyEnum]](_: T): auto = const n = span(T) - var ret: array[n, uint8] var i = 0 assert n <= invalidSlot.int + var ret {.noinit.}: array[n, uint8] for ai in mitems(ret): ai = invalidSlot for ai in items(T): ret[ai.ord - T.low.ord] = uint8(i) @@ -172,6 +174,9 @@ template symbolRank*[T: enum](a: T): int = when T is Ordinal: ord(a) - T.low.ord.static else: symbolRankImpl(a) +proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".} + # skip one level of range; return the base type of a range type + func symbolName*[T: enum](a: T): string = ## Returns the symbol name of an enum. ## @@ -190,5 +195,8 @@ func symbolName*[T: enum](a: T): string = c1 = 4 c2 = 20 assert c1.symbolName == "c1" - const names = enumNames(T) + when T is range: + const names = enumNames(rangeBase T) + else: + const names = enumNames(T) names[a.symbolRank] diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim index 46595a3cf..a955077ea 100644 --- a/lib/std/envvars.nim +++ b/lib/std/envvars.nim @@ -60,12 +60,16 @@ when not defined(nimscript): when defined(windows): proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "<stdlib.h>".} from std/private/win_setenv import setEnvImpl - import winlean + import std/winlean when defined(nimPreviewSlimSystem): import std/widestrs - proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", + + type wchar_t {.importc: "wchar_t", header: "<stdlib.h>".} = int16 + proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "<stdlib.h>".} - proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString) + proc getEnvImpl(env: cstring): WideCString = + let r: WideCString = env.newWideCString + cast[WideCString](c_wgetenv(cast[ptr wchar_t](r))) else: proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "<stdlib.h>".} @@ -90,8 +94,10 @@ when not defined(nimscript): assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" let env = getEnvImpl(key) - if env == nil: return default - result = $env + if env == nil: + result = default + else: + result = $env proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = ## Checks whether the environment variable named `key` exists. @@ -105,7 +111,7 @@ when not defined(nimscript): runnableExamples: assert not existsEnv("unknownEnv") - return getEnvImpl(key) != nil + result = getEnvImpl(key) != nil proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## Sets the value of the `environment variable`:idx: named `key` to `val`. @@ -136,7 +142,7 @@ when not defined(nimscript): ## * `envPairs iterator`_ template bail = raiseOSError(osLastError(), key) when defined(windows): - #[ + #[ # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string note that nil is not legal @@ -173,20 +179,17 @@ when not defined(nimscript): iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = when defined(windows): - block implBlock: - template impl(get_fun, typ, size, zero, free_fun) = - let env = get_fun() - var e = env - if e == nil: break implBlock - while true: - let eend = strEnd(e) - let kv = $e - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - e = cast[typ](cast[ByteAddress](eend)+size) - if typeof(zero)(eend[1]) == zero: break - discard free_fun(env) - impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) + let env = getEnvironmentStringsW() + var e = env + if e != nil: + while true: + let eend = strEnd(e) + let kv = $e + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + e = cast[WideCString](cast[ByteAddress](eend)+2) + if int(eend[1]) == 0: break + discard freeEnvironmentStringsW(env) else: var i = 0 when defined(macosx) and not defined(ios) and not defined(emscripten): diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index d63c1abc5..f26368f42 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -9,7 +9,9 @@ ## This module allows adding hooks to program exit. -import locks +import std/locks +when defined(js) and not defined(nodejs): + import std/assertions type FunKind = enum kClosure, kNoconv # extend as needed @@ -20,20 +22,20 @@ type var gFunsLock: Lock - gFuns: seq[Fun] + gFuns {.cursor.}: seq[Fun] #Intentionally use the cursor to break up the lifetime trace and make it compatible with JS. initLock(gFunsLock) when defined(js): proc addAtExit(quitProc: proc() {.noconv.}) = when defined(nodejs): - asm """ + {.emit: """ process.on('exit', `quitProc`); - """ + """.} elif defined(js): - asm """ + {.emit: """ window.onbeforeunload = `quitProc`; - """ + """.} else: proc addAtExit(quitProc: proc() {.noconv.}) {. importc: "atexit", header: "<stdlib.h>".} @@ -45,6 +47,7 @@ proc callClosures() {.noconv.} = case fun.kind of kClosure: fun.fun1() of kNoconv: fun.fun2() + gFuns.setLen(0) template fun() = if gFuns.len == 0: @@ -66,24 +69,19 @@ proc addExitProc*(cl: proc() {.noconv.}) = fun() gFuns.add Fun(kind: kNoconv, fun2: cl) -when not defined(nimscript): +when not defined(nimscript) and (not defined(js) or defined(nodejs)): proc getProgramResult*(): int = when defined(js) and defined(nodejs): - asm """ + {.emit: """ `result` = process.exitCode; -""" - elif not defined(js): - result = programResult +""".} else: - doAssert false + result = programResult proc setProgramResult*(a: int) = - # pending https://github.com/nim-lang/Nim/issues/14674 when defined(js) and defined(nodejs): - asm """ + {.emit: """ process.exitCode = `a`; -""" - elif not defined(js): - programResult = a +""".} else: - doAssert false + programResult = a diff --git a/lib/std/files.nim b/lib/std/files.nim index 138bb5234..c4e0491c9 100644 --- a/lib/std/files.nim +++ b/lib/std/files.nim @@ -1,12 +1,15 @@ ## This module implements file handling. +## +## **See also:** +## * `paths module <paths.html>`_ for path manipulation -from paths import Path, ReadDirEffect, WriteDirEffect +from std/paths import Path, ReadDirEffect, WriteDirEffect from std/private/osfiles import fileExists, removeFile, moveFile -proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect].} = +proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} = ## Returns true if `filename` exists and is a regular file or symlink. ## ## Directories, device files, named pipes and sockets return false. diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index 8abbe59cd..9258245f6 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -11,8 +11,6 @@ when defined(nimPreviewSlimSystem): import std/assertions -else: - {.deprecated: "formatfloat is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/formatfloat`".} proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.} @@ -37,8 +35,8 @@ proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int result = float32ToChars(buf, value, forceTrailingDotZero=true).int buf[result] = '\0' -proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", - importc: "sprintf", varargs, noSideEffect.} +proc c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>", + importc: "snprintf", varargs, noSideEffect.} proc writeToBuffer(buf: var array[65, char]; value: cstring) = var i = 0 @@ -51,7 +49,7 @@ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): ## ## returns the amount of bytes written to `buf` not counting the ## terminating '\0' character. - var n = c_sprintf(cast[cstring](addr buf), "%.16g", value).int + var n = c_snprintf(cast[cstring](addr buf), 65, "%.16g", value).int var hasDot = false for i in 0..n-1: if buf[i] == ',': @@ -88,7 +86,7 @@ proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32 proc addFloatRoundtrip*(result: var string; x: float | float32) = when nimvm: - doAssert false + raiseAssert "unreachable" else: var buffer {.noinit.}: array[65, char] let n = writeFloatToBufferRoundtrip(buffer, x) @@ -96,28 +94,29 @@ proc addFloatRoundtrip*(result: var string; x: float | float32) = proc addFloatSprintf*(result: var string; x: float) = when nimvm: - doAssert false + raiseAssert "unreachable" else: var buffer {.noinit.}: array[65, char] let n = writeFloatToBufferSprintf(buffer, x) result.addCstringN(cast[cstring](buffer[0].addr), n) -proc nimFloatToString(a: float): cstring = - ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 - # print `-0.0` properly - asm """ - function nimOnlyDigitsOrMinus(n) { - return n.toString().match(/^-?\d+$/); - } - if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" - else { - `result` = `a`+"" - if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" +when defined(js): + proc nimFloatToString(a: float): cstring = + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly + {.emit: """ + function nimOnlyDigitsOrMinus(n) { + return n.toString().match(/^-?\d+$/); } - } - """ + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"; + else { + `result` = `a`+""; + if(nimOnlyDigitsOrMinus(`result`)){ + `result` = `a`+".0"; + } + } + """.} proc addFloat*(result: var string; x: float | float32) {.inline.} = ## Converts float to its string representation and appends it to `result`. diff --git a/lib/std/genasts.nim b/lib/std/genasts.nim index 05b2823ef..d0f07c527 100644 --- a/lib/std/genasts.nim +++ b/lib/std/genasts.nim @@ -1,6 +1,6 @@ ## This module implements AST generation using captured variables for macros. -import macros +import std/macros type GenAstOpt* = enum kDirtyTemplate, @@ -24,7 +24,7 @@ macro genAstOpt*(options: static set[GenAstOpt], args: varargs[untyped]): untype result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]): # each local symbol we access must be explicitly captured if not cond: - doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] + raiseAssert "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] let a = 3 check2 a*2 == a+3 if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4' diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim index 7d6ac6092..b03e00651 100644 --- a/lib/std/isolation.nim +++ b/lib/std/isolation.nim @@ -15,7 +15,7 @@ ## type - Isolated*[T] = object ## Isolated data can only be moved, not copied. + Isolated*[T] {.sendable.} = object ## Isolated data can only be moved, not copied. value: T proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.} @@ -38,9 +38,9 @@ func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".} = func unsafeIsolate*[T](value: sink T): Isolated[T] = ## Creates an isolated subgraph from the expression `value`. - ## + ## ## .. warning:: The proc doesn't check whether `value` is isolated. - ## + ## Isolated[T](value: value) func extract*[T](src: var Isolated[T]): T = diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim index 04578fc87..4e996ea7b 100644 --- a/lib/std/jsbigints.nim +++ b/lib/std/jsbigints.nim @@ -14,7 +14,7 @@ func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} = runnableExamples: doAssert big(1234567890) == big"1234567890" doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} = ## Constructor for `JsBigInt`. @@ -28,11 +28,11 @@ func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} = doAssert 0xdeadbeaf'big == 0xdeadbeaf.big doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big doAssert not compiles(static(12'big)) - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} = ## Alias for `'big` - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} = ## Converts from `JsBigInt` to `cstring` representation. @@ -64,10 +64,10 @@ func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs: runnableExamples: doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3") -func toNumber*(this: JsBigInt): BiggestInt {.importjs: "Number(#)".} = +func toNumber*(this: JsBigInt): int {.importjs: "Number(#)".} = ## Does not do any bounds check and may or may not return an inexact representation. runnableExamples: - doAssert toNumber(big"2147483647") == 2147483647.BiggestInt + doAssert toNumber(big"2147483647") == 2147483647.int func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} = runnableExamples: @@ -110,7 +110,7 @@ func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} = doAssert big"42" == big"42" func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} = - # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812 + # (#) needed due to unary minus runnableExamples: doAssert big"2" ** big"64" == big"18446744073709551616" doAssert big"-2" ** big"3" == big"-8" @@ -120,8 +120,6 @@ func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} = try: discard big"2" ** big"-1" # raises foreign `RangeError` except: ok = true doAssert ok - # pending https://github.com/nim-lang/Nim/pull/15940, simplify to: - # doAssertRaises: discard big"2" ** big"-1" # raises foreign `RangeError` func `and`*(x, y: JsBigInt): JsBigInt {.importjs: "(# & #)".} = runnableExamples: diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index eec8dea7d..2d28748ce 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -16,7 +16,7 @@ runnableExamples: assert 0.0.toJson.kind == JFloat assert Inf.toJson.kind == JString -import json, strutils, tables, sets, strtabs, options, strformat +import std/[json, strutils, tables, sets, strtabs, options, strformat] #[ Future directions: @@ -30,29 +30,15 @@ add a way to customize serialization, for e.g.: objects. ]# -import macros -from enumutils import symbolName -from typetraits import OrdinalEnum, tupleLen +import std/macros +from std/enumutils import symbolName +from std/typetraits import OrdinalEnum, tupleLen when defined(nimPreviewSlimSystem): import std/assertions -when not defined(nimFixedForwardGeneric): - # xxx remove pending csources_v1 update >= 1.2.0 - proc to[T](node: JsonNode, t: typedesc[T]): T = - when T is string: node.getStr - elif T is bool: node.getBool - else: static: doAssert false, $T # support as needed (only needed during bootstrap) - proc isNamedTuple(T: typedesc): bool = # old implementation - when T isnot tuple: result = false - else: - var t: T - for name, _ in t.fieldPairs: - when name == "Field0": return compiles(t.Field0) - else: return true - return false -else: - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} + +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} type Joptions* = object # xxx rename FromJsonOptions @@ -92,19 +78,24 @@ macro getDiscriminants(a: typedesc): seq[string] = let sym = a[1] let t = sym.getTypeImpl let t2 = t[2] - doAssert t2.kind == nnkRecList - result = newTree(nnkBracket) - for ti in t2: - if ti.kind == nnkRecCase: - let key = ti[0][0] - let typ = ti[0][1] - result.add newLit key.strVal - if result.len > 0: + case t2.kind + of nnkEmpty: # allow empty objects result = quote do: - @`result` + seq[string].default + of nnkRecList: + result = newTree(nnkBracket) + for ti in t2: + if ti.kind == nnkRecCase: + let key = ti[0][0] + result.add newLit key.strVal + if result.len > 0: + result = quote do: + @`result` + else: + result = quote do: + seq[string].default else: - result = quote do: - seq[string].default + raiseAssert "unexpected kind: " & $t2.kind macro initCaseObject(T: typedesc, fun: untyped): untyped = ## does the minimum to construct a valid case object, only initializing @@ -118,7 +109,7 @@ macro initCaseObject(T: typedesc, fun: untyped): untyped = case t.kind of nnkObjectTy: t2 = t[2] of nnkRefTy: t2 = t[0].getTypeImpl[2] - else: doAssert false, $t.kind # xxx `nnkPtrTy` could be handled too + else: raiseAssert $t.kind # xxx `nnkPtrTy` could be handled too doAssert t2.kind == nnkRecList result = newTree(nnkObjConstr) result.add sym @@ -231,12 +222,7 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = elif T is uint|uint64: a = T(to(b, uint64)) elif T is Ordinal: a = cast[T](to(b, int)) elif T is pointer: a = cast[pointer](to(b, int)) - elif T is distinct: - when nimvm: - # bug, potentially related to https://github.com/nim-lang/Nim/issues/12282 - a = T(jsonTo(b, distinctBase(T))) - else: - a.distinctBase.fromJson(b) + elif T is distinct: a.distinctBase.fromJson(b) elif T is string|SomeNumber: a = to(b,T) elif T is cstring: case b.kind @@ -303,7 +289,7 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = i.inc else: # checkJson not appropriate here - static: doAssert false, "not yet implemented: " & $T + static: raiseAssert "not yet implemented: " & $T proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T = ## reverse of `toJson` @@ -385,7 +371,6 @@ proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V]), op ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,,JsonNode>`_ - # pending PR #9217 use: toSeq(a) instead of `collect` in `runnableExamples`. runnableExamples: import std/[tables, json, sugar] let foo = ( diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index 5c67a5d4c..bf6dc776b 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -36,7 +36,7 @@ See also * `times module <times.html>`_ ]## -import times +import std/times type MonoTime* = object ## Represents a monotonic timestamp. @@ -74,7 +74,7 @@ when defined(js): {.pop.} elif defined(posix) and not defined(osx): - import posix + import std/posix when defined(zephyr): proc k_uptime_ticks(): int64 {.importc: "k_uptime_ticks", header: "<kernel.h>".} diff --git a/lib/std/oserrors.nim b/lib/std/oserrors.nim index 8d06c41da..7b11c5e8e 100644 --- a/lib/std/oserrors.nim +++ b/lib/std/oserrors.nim @@ -15,7 +15,7 @@ type when not defined(nimscript): when defined(windows): - import winlean + import std/winlean when defined(nimPreviewSlimSystem): import std/widestrs else: @@ -75,17 +75,14 @@ proc newOSError*( ## See also: ## * `osErrorMsg proc`_ ## * `osLastError proc`_ - var e: owned(ref OSError); new(e) - e.errorCode = errorCode.int32 - e.msg = osErrorMsg(errorCode) + result = (ref OSError)(errorCode: errorCode.int32, msg: osErrorMsg(errorCode)) if additionalInfo.len > 0: - if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n' - e.msg.add "Additional info: " - e.msg.add additionalInfo + if result.msg.len > 0 and result.msg[^1] != '\n': result.msg.add '\n' + result.msg.add "Additional info: " + result.msg.add additionalInfo # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs. - if e.msg == "": - e.msg = "unknown OS error" - return e + if result.msg == "": + result.msg = "unknown OS error" proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = ## Raises an `OSError exception <system.html#OSError>`_. diff --git a/lib/std/outparams.nim b/lib/std/outparams.nim index 8a0e5ae67..a471fbaa7 100644 --- a/lib/std/outparams.nim +++ b/lib/std/outparams.nim @@ -9,7 +9,7 @@ ## `outParamsAt` macro for easy writing code that works with both 2.0 and 1.x. -import macros +import std/macros macro outParamsAt*(positions: static openArray[int]; n: untyped): untyped = ## Use this macro to annotate `out` parameters in a portable way. diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index 1e2892658..3320558f2 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -12,17 +12,12 @@ ## ## Supports any Ordinal type. ## -## .. note:: Currently the assignment operator `=` for `PackedSet[A]` -## performs some rather meaningless shallow copy. Since Nim currently does -## not allow the assignment operator to be overloaded, use the `assign proc -## <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy. -## ## See also ## ======== ## * `sets module <sets.html>`_ for more general hash sets import std/private/since -import hashes +import std/hashes when defined(nimPreviewSlimSystem): import std/assertions @@ -114,7 +109,6 @@ proc intSetPut[A](t: var PackedSet[A], key: int): Trunk = t.data[h] = result proc bitincl[A](s: var PackedSet[A], key: int) {.inline.} = - var ret: Trunk var t = intSetPut(s, key shr TrunkShift) var u = key and TrunkMask t.bits[u shr IntShift] = t.bits[u shr IntShift] or @@ -204,6 +198,7 @@ proc contains*[A](s: PackedSet[A], key: A): bool = assert B notin letters if s.elems <= s.a.len: + result = false for i in 0..<s.elems: if s.a[i] == ord(key): return true else: @@ -412,18 +407,9 @@ proc isNil*[A](x: PackedSet[A]): bool {.inline.} = x.head.isNil and x.elems == 0 -proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = +proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) = ## Copies `src` to `dest`. ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_. - runnableExamples: - var - a = initPackedSet[int]() - b = initPackedSet[int]() - b.incl(5) - b.incl(7) - a.assign(b) - assert len(a) == 2 - if src.elems <= src.a.len: dest.data = @[] dest.max = 0 @@ -452,6 +438,19 @@ proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = dest.data[h] = n it = it.next +proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) {.inline, deprecated.} = + ## Copies `src` to `dest`. + ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + b.incl(5) + b.incl(7) + a.assign(b) + assert len(a) == 2 + `=copy`(dest, src) + proc union*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## Returns the union of the sets `s1` and `s2`. ## diff --git a/lib/std/paths.nim b/lib/std/paths.nim index c29096982..664dedd31 100644 --- a/lib/std/paths.nim +++ b/lib/std/paths.nim @@ -1,4 +1,7 @@ ## This module implements path handling. +## +## **See also:** +## * `files module <files.html>`_ for file access import std/private/osseps export osseps @@ -6,7 +9,7 @@ export osseps import std/envvars import std/private/osappdirs -import pathnorm +import std/[pathnorm, hashes, sugar, strutils] from std/private/ospaths2 import joinPath, splitPath, ReadDirEffect, WriteDirEffect, @@ -22,6 +25,16 @@ export ReadDirEffect, WriteDirEffect type Path* = distinct string +func hash*(x: Path): Hash = + let x = x.string.dup(normalizePath) + if FileSystemCaseSensitive: + result = x.hash + else: + result = x.toLowerAscii.hash + +template `$`*(x: Path): string = + string(x) + func `==`*(x, y: Path): bool {.inline.} = ## Compares two paths. ## diff --git a/lib/std/private/dbutils.nim b/lib/std/private/dbutils.nim deleted file mode 100644 index 0ae3b3702..000000000 --- a/lib/std/private/dbutils.nim +++ /dev/null @@ -1,15 +0,0 @@ -import db_common - - -template dbFormatImpl*(formatstr: SqlQuery, dbQuote: proc (s: string): string, args: varargs[string]): string = - var res = "" - var a = 0 - for c in items(string(formatstr)): - if c == '?': - if a == args.len: - dbError("""The number of "?" given exceeds the number of parameters present in the query.""") - add(res, dbQuote(args[a])) - inc(a) - else: - add(res, c) - res diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index b3dc5d14f..f2d0d25cb 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -19,7 +19,7 @@ const # Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c # Generates: -# .. code-block:: nim +# ```nim # var res = "" # for i in 0 .. 99: # if i < 10: @@ -27,14 +27,15 @@ const # else: # res.add $i # doAssert res == digits100 +# ``` proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = buf[pos] = digits100[2 * digits] buf[pos+1] = digits100[2 * digits + 1] #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) -proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = - return trailingZeros100[digits.int8] +proc trailingZeros2Digits*(digits: uint32): int {.inline.} = + trailingZeros100[digits] when defined(js): proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".} @@ -101,9 +102,7 @@ proc addInt*(result: var string; x: int64) {.enforceNoRaises.} = num = cast[uint64](x) else: num = uint64(-x) - let base = result.len - setLen(result, base + 1) - result[base] = '-' + result.add '-' else: num = uint64(x) addInt(result, num) diff --git a/lib/std/private/dragonbox.nim b/lib/std/private/dragonbox.nim index 2ba22a751..85ffea84a 100644 --- a/lib/std/private/dragonbox.nim +++ b/lib/std/private/dragonbox.nim @@ -75,10 +75,10 @@ const const signMask*: BitsType = not (not BitsType(0) shr 1) -proc constructDouble*(bits: BitsType): Double {.constructor.} = +proc constructDouble*(bits: BitsType): Double = result.bits = bits -proc constructDouble*(value: ValueType): Double {.constructor.} = +proc constructDouble*(value: ValueType): Double = result.bits = cast[typeof(result.bits)](value) proc physicalSignificand*(this: Double): BitsType {.noSideEffect.} = @@ -1052,7 +1052,7 @@ when false: proc memset(x: cstring; ch: char; L: int) {.importc, nodecl.} proc memmove(a, b: cstring; L: int) {.importc, nodecl.} -proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int32 {.inline.} = +proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int {.inline.} = dragonbox_Assert(digits >= 1) dragonbox_Assert(digits <= 99999999'u32) let q: uint32 = digits div 10000 @@ -1070,12 +1070,12 @@ proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: u utoa2Digits(buf, pos + 6, rL) return trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0) -proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int32 {.inline.} = +proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int {.inline.} = var pos = pos var output64 = output64 - var tz: int32 = 0 + var tz = 0 ## number of trailing zeros removed. - var nd: int32 = 0 + var nd = 0 ## number of decimal digits processed. ## At most 17 digits remaining if output64 >= 100000000'u64: @@ -1220,7 +1220,7 @@ proc formatDigits*[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint ## dE+123 or d.igitsE+123 decimalDigitsPosition = 1 var digitsEnd = pos + int(decimalDigitsPosition + numDigits) - let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) + let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits) dec(digitsEnd, tz) dec(numDigits, tz) ## decimal_exponent += tz; // => decimal_point unchanged. diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim index db323bee1..6dc9c8f3b 100644 --- a/lib/std/private/gitutils.nim +++ b/lib/std/private/gitutils.nim @@ -4,7 +4,7 @@ internal API for now, API subject to change # xxx move other git utilities here; candidate for stdlib. -import std/[os, osproc, strutils, tempfiles] +import std/[os, paths, osproc, strutils, tempfiles] when defined(nimPreviewSlimSystem): import std/[assertions, syncio] @@ -32,15 +32,8 @@ template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool = result proc isGitRepo*(dir: string): bool = - ## This command is used to get the relative path to the root of the repository. - ## Using this, we can verify whether a folder is a git repository by checking - ## whether the command success and if the output is empty. - let (output, status) = execCmdEx("git rev-parse --show-cdup", workingDir = dir) - # On Windows there will be a trailing newline on success, remove it. - # The value of a successful call typically won't have a whitespace (it's - # usually a series of ../), so we know that it's safe to unconditionally - # remove trailing whitespaces from the result. - result = status == 0 and output.strip() == "" + ## Avoid calling git since it depends on /bin/sh existing and fails in Nix. + return fileExists(dir/".git/HEAD") proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] = ## Returns a human readable diff of files `path1`, `path2`, the exact form of diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index 5e3e33cb4..a6d088558 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -4,9 +4,9 @@ this can eventually be moved to std/os and `walkDirRec` can be implemented in te to avoid duplication ]## -import os +import std/os when defined(windows): - from strutils import replace + from std/strutils import replace when defined(nimPreviewSlimSystem): import std/[assertions, objectdollar] @@ -60,11 +60,11 @@ proc nativeToUnixPath*(path: string): string = result[0] = '/' result[1] = path[0] if path.len > 2 and path[2] != '\\': - doAssert false, "paths like `C:foo` are currently unsupported, path: " & path + raiseAssert "paths like `C:foo` are currently unsupported, path: " & path when DirSep == '\\': result = replace(result, '\\', '/') when isMainModule: - import sugar + import std/sugar for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", "csources_v1", "csources", "bin"]): echo a diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index 836b3512a..5f79eab27 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -37,13 +37,13 @@ when defined(js): let a = array[2, float64].default assert jsConstructorName(a) == "Float64Array" assert jsConstructorName(a.toJs) == "Float64Array" - asm """`result` = `a`.constructor.name""" + {.emit: """`result` = `a`.constructor.name;""".} proc hasJsBigInt*(): bool = - asm """`result` = typeof BigInt != 'undefined'""" + {.emit: """`result` = typeof BigInt != 'undefined';""".} proc hasBigUint64Array*(): bool = - asm """`result` = typeof BigUint64Array != 'undefined'""" + {.emit: """`result` = typeof BigUint64Array != 'undefined';""".} proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} = runnableExamples: @@ -79,5 +79,18 @@ when defined(js): assert not "123".toJs.isSafeInteger assert 123.isSafeInteger assert 123.toJs.isSafeInteger - assert 9007199254740991.toJs.isSafeInteger - assert not 9007199254740992.toJs.isSafeInteger + when false: + assert 9007199254740991.toJs.isSafeInteger + assert not 9007199254740992.toJs.isSafeInteger + +template whenJsNoBigInt64*(no64, yes64): untyped = + when defined(js): + when compiles(compileOption("jsbigint64")): + when compileOption("jsbigint64"): + yes64 + else: + no64 + else: + no64 + else: + no64 diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim index 47b788ee9..06fda6fa1 100644 --- a/lib/std/private/miscdollars.nim +++ b/lib/std/private/miscdollars.nim @@ -13,21 +13,7 @@ template toLocation*(result: var string, file: string | cstring, line: int, col: addInt(result, col) result.add ")" -when defined(nimHasIsNamedTuple): - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} -else: - # for bootstrap; remove after release 1.2 - proc isNamedTuple(T: typedesc): bool = - # Taken from typetraits. - when T isnot tuple: result = false - else: - var t: T - for name, _ in t.fieldPairs: - when name == "Field0": - return compiles(t.Field0) - else: - return true - return false +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} template tupleObjectDollar*[T: tuple | object](result: var string, x: T) = result = "(" diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index 8e6ae00f8..07a6809bb 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -1,3 +1,5 @@ +## .. importdoc:: paths.nim, dirs.nim + include system/inclrtl import std/envvars import std/private/ospaths2 @@ -10,6 +12,7 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", ## for the convenience of processing paths coming from user configuration files. ## ## See also: + ## * `getDataDir proc`_ ## * `getConfigDir proc`_ ## * `getTempDir proc`_ ## * `expandTilde proc`_ @@ -22,6 +25,30 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", when defined(windows): return getEnv("USERPROFILE") & "\\" else: return getEnv("HOME") & "/" +proc getDataDir*(): string {.rtl, extern: "nos$1" + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the data directory of the current user for applications. + ## + ## On non-Windows OSs, this proc conforms to the XDG Base Directory + ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment + ## variable if it is set, otherwise it returns the default configuration + ## directory ("~/.local/share" or "~/Library/Application Support" on macOS). + ## + ## See also: + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `expandTilde proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ + when defined(windows): + result = getEnv("APPDATA") + elif defined(macosx): + result = getEnv("XDG_DATA_HOME", getEnv("HOME") / "Library" / "Application Support") + else: + result = getEnv("XDG_DATA_HOME", getEnv("HOME") / ".local" / "share") + result.normalizePathEnd(trailingSep = true) + proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. @@ -36,6 +63,7 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", ## ## See also: ## * `getHomeDir proc`_ + ## * `getDataDir proc`_ ## * `getTempDir proc`_ ## * `expandTilde proc`_ ## * `getCurrentDir proc`_ @@ -61,6 +89,7 @@ proc getCacheDir*(): string = ## * `getHomeDir proc`_ ## * `getTempDir proc`_ ## * `getConfigDir proc`_ + ## * `getDataDir proc`_ # follows https://crates.io/crates/platform-dirs when defined(windows): result = getEnv("LOCALAPPDATA") diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim index 5b4e1d7e3..c49d52ef2 100644 --- a/lib/std/private/oscommon.nim +++ b/lib/std/private/oscommon.nim @@ -5,6 +5,8 @@ import std/[oserrors] when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] +## .. importdoc:: osdirs.nim, os.nim + const weirdTarget* = defined(nimscript) or defined(js) @@ -20,9 +22,9 @@ type when weirdTarget: discard elif defined(windows): - import winlean, times + import std/[winlean, times] elif defined(posix): - import posix + import std/posix proc c_rename(oldname, newname: cstring): cint {. importc: "rename", header: "<stdio.h>".} else: @@ -45,23 +47,17 @@ else: when defined(windows) and not weirdTarget: - when useWinUnicode: - template wrapUnary*(varname, winApiProc, arg: untyped) = - var varname = winApiProc(newWideCString(arg)) - - template wrapBinary*(varname, winApiProc, arg, arg2: untyped) = - var varname = winApiProc(newWideCString(arg), arg2) - proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = - result = findFirstFileW(newWideCString(a), b) - template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) - - template getFilename*(f: untyped): untyped = - $cast[WideCString](addr(f.cFileName[0])) - else: - template findFirstFile*(a, b: untyped): untyped = findFirstFileA(a, b) - template findNextFile*(a, b: untyped): untyped = findNextFileA(a, b) + template wrapUnary*(varname, winApiProc, arg: untyped) = + var varname = winApiProc(newWideCString(arg)) + + template wrapBinary*(varname, winApiProc, arg, arg2: untyped) = + var varname = winApiProc(newWideCString(arg), arg2) + proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = + result = findFirstFileW(newWideCString(a), b) + template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) - template getFilename*(f: untyped): untyped = $cast[cstring](addr f.cFileName) + template getFilename*(f: untyped): untyped = + $cast[WideCString](addr(f.cFileName[0])) proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} = # Note - takes advantage of null delimiter in the cstring @@ -102,12 +98,9 @@ proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} ## In case of other errors `OSError` is raised. ## Returns true in case of success. when defined(windows): - when useWinUnicode: - let s = newWideCString(source) - let d = newWideCString(dest) - result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 - else: - result = moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 + let s = newWideCString(source) + let d = newWideCString(dest) + result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 else: result = c_rename(source, dest) == 0'i32 @@ -126,7 +119,7 @@ when not defined(windows): const maxSymlinkLen* = 1024 proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect], noNimJs.} = + tags: [ReadDirEffect], noNimJs, sideEffect.} = ## Returns true if `filename` exists and is a regular file or symlink. ## ## Directories, device files, named pipes and sockets return false. @@ -135,10 +128,7 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", ## * `dirExists proc`_ ## * `symlinkExists proc`_ when defined(windows): - when useWinUnicode: - wrapUnary(a, getFileAttributesW, filename) - else: - var a = getFileAttributesA(filename) + wrapUnary(a, getFileAttributesW, filename) if a != -1'i32: result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32 else: @@ -147,7 +137,7 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], - noNimJs.} = + noNimJs, sideEffect.} = ## Returns true if the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. ## @@ -155,10 +145,7 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] ## * `fileExists proc`_ ## * `symlinkExists proc`_ when defined(windows): - when useWinUnicode: - wrapUnary(a, getFileAttributesW, dir) - else: - var a = getFileAttributesA(dir) + wrapUnary(a, getFileAttributesW, dir) if a != -1'i32: result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 else: @@ -168,7 +155,7 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], - noWeirdTarget.} = + noWeirdTarget, sideEffect.} = ## Returns true if the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. ## @@ -176,10 +163,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", ## * `fileExists proc`_ ## * `dirExists proc`_ when defined(windows): - when useWinUnicode: - wrapUnary(a, getFileAttributesW, link) - else: - var a = getFileAttributesA(link) + wrapUnary(a, getFileAttributesW, link) if a != -1'i32: # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK` # may also be needed. @@ -195,15 +179,8 @@ when defined(windows) and not weirdTarget: flags = flags or FILE_FLAG_OPEN_REPARSE_POINT let access = if writeAccess: GENERIC_WRITE else: 0'i32 - when useWinUnicode: - result = createFileW( - newWideCString(path), access, - FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, - nil, OPEN_EXISTING, flags, 0 - ) - else: - result = createFileA( - path, access, - FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, - nil, OPEN_EXISTING, flags, 0 - ) + result = createFileW( + newWideCString(path), access, + FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, + nil, OPEN_EXISTING, flags, 0 + ) diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index add9ed424..a44cad7d9 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -1,3 +1,5 @@ +## .. importdoc:: osfiles.nim, appdirs.nim, paths.nim + include system/inclrtl import std/oserrors @@ -14,9 +16,9 @@ when defined(nimPreviewSlimSystem): when weirdTarget: discard elif defined(windows): - import winlean, times + import std/[winlean, times] elif defined(posix): - import posix, times + import std/[posix, times] else: {.error: "OS module not ported to your operating system!".} @@ -326,10 +328,7 @@ iterator walkDirRec*(dir: string, proc rawRemoveDir(dir: string) {.noWeirdTarget.} = when defined(windows): - when useWinUnicode: - wrapUnary(res, removeDirectoryW, dir) - else: - var res = removeDirectoryA(dir) + wrapUnary(res, removeDirectoryW, dir) let lastError = osLastError() if res == 0'i32 and lastError.int32 != 3'i32 and lastError.int32 != 18'i32 and lastError.int32 != 2'i32: @@ -394,10 +393,7 @@ proc rawCreateDir(dir: string): bool {.noWeirdTarget.} = #echo res raiseOSError(osLastError(), dir) else: - when useWinUnicode: - wrapUnary(res, createDirectoryW, dir) - else: - let res = createDirectoryA(dir) + wrapUnary(res, createDirectoryW, dir) if res != 0'i32: result = true @@ -450,13 +446,17 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", else: discard existsOrCreateDir(p) -proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", +proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest`. ## ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## + ## If `skipSpecial` is true, then (besides all directories) only *regular* + ## files (**without** special "file" objects like FIFOs, device files, + ## etc) will be copied on Unix. + ## ## If this fails, `OSError` is raised. ## ## On the Windows platform this proc will copy the attributes from @@ -476,16 +476,17 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", ## * `createDir proc`_ ## * `moveDir proc`_ createDir(dest) - for kind, path in walkDir(source): + for kind, path in walkDir(source, skipSpecial = skipSpecial): var noSource = splitPath(path).tail if kind == pcDir: - copyDir(path, dest / noSource) + copyDir(path, dest / noSource, skipSpecial = skipSpecial) else: copyFile(path, dest / noSource, {cfSymlinkAsIs}) proc copyDirWithPermissions*(source, dest: string, - ignorePermissionErrors = true) + ignorePermissionErrors = true, + skipSpecial = false) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest` preserving file permissions. @@ -493,6 +494,10 @@ proc copyDirWithPermissions*(source, dest: string, ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## + ## If `skipSpecial` is true, then (besides all directories) only *regular* + ## files (**without** special "file" objects like FIFOs, device files, + ## etc) will be copied on Unix. + ## ## If this fails, `OSError` is raised. This is a wrapper proc around ## `copyDir`_ and `copyFileWithPermissions`_ procs ## on non-Windows platforms. @@ -522,10 +527,10 @@ proc copyDirWithPermissions*(source, dest: string, except: if not ignorePermissionErrors: raise - for kind, path in walkDir(source): + for kind, path in walkDir(source, skipSpecial = skipSpecial): var noSource = splitPath(path).tail if kind == pcDir: - copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors) + copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial) else: copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs}) @@ -559,10 +564,7 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = ## * `getTempDir proc`_ ## * `getCurrentDir proc`_ when defined(windows): - when useWinUnicode: - if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: - raiseOSError(osLastError(), newDir) - else: - if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError(), newDir) + if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: + raiseOSError(osLastError(), newDir) else: if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir) diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index 301f14600..37d8eabca 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -7,6 +7,7 @@ export fileExists import ospaths2, ossymlinks +## .. importdoc:: osdirs.nim, os.nim when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] @@ -14,9 +15,9 @@ when defined(nimPreviewSlimSystem): when weirdTarget: discard elif defined(windows): - import winlean + import std/winlean elif defined(posix): - import posix, times + import std/[posix, times] proc toTime(ts: Timespec): times.Time {.inline.} = result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) @@ -83,10 +84,7 @@ proc getFilePermissions*(filename: string): set[FilePermission] {. if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite) if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec) else: - when useWinUnicode: - wrapUnary(res, getFileAttributesW, filename) - else: - var res = getFileAttributesA(filename) + wrapUnary(res, getFileAttributesW, filename) if res == -1'i32: raiseOSError(osLastError(), filename) if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, @@ -135,19 +133,13 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission], if chmod(filename, cast[Mode](p)) != 0: raiseOSError(osLastError(), $(filename, permissions)) else: - when useWinUnicode: - wrapUnary(res, getFileAttributesW, filename) - else: - var res = getFileAttributesA(filename) + wrapUnary(res, getFileAttributesW, filename) if res == -1'i32: raiseOSError(osLastError(), filename) if fpUserWrite in permissions: res = res and not FILE_ATTRIBUTE_READONLY else: res = res or FILE_ATTRIBUTE_READONLY - when useWinUnicode: - wrapBinary(res2, setFileAttributesW, filename, res) - else: - var res2 = setFileAttributesA(filename, res) + wrapBinary(res2, setFileAttributesW, filename, res) if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions)) @@ -163,10 +155,14 @@ when hasCCopyfile: 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 + when (NimMajor, NimMinor) >= (1, 4): + let + COPYFILE_DATA {.nodecl.}: copyfile_flags_t + COPYFILE_XATTR {.nodecl.}: copyfile_flags_t + else: + var + COPYFILE_DATA {.nodecl.}: copyfile_flags_t + COPYFILE_XATTR {.nodecl.}: copyfile_flags_t {.pop.} type @@ -177,7 +173,7 @@ type const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore} -proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, +proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 16_384) {.rtl, extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} = ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist. @@ -206,6 +202,8 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, ## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless ## `-d:nimLegacyCopyFile` is used. ## + ## `copyFile` allows to specify `bufferSize` to improve I/O performance. + ## ## See also: ## * `CopyFlag enum`_ ## * `copyDir proc`_ @@ -214,20 +212,15 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, ## * `removeFile proc`_ ## * `moveFile proc`_ - doAssert card(copyFlagSymlink * options) == 1, "There should be exactly " & - "one cfSymlink* in options" + doAssert card(copyFlagSymlink * options) == 1, "There should be exactly one cfSymlink* in options" let isSymlink = source.symlinkExists if isSymlink and (cfSymlinkIgnore in options or defined(windows)): return when defined(windows): - when useWinUnicode: - let s = newWideCString(source) - let d = newWideCString(dest) - 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)) + let s = newWideCString(source) + let d = newWideCString(dest) + if copyFileW(s, d, 0'i32) == 0'i32: + raiseOSError(osLastError(), $(source, dest)) else: if isSymlink and cfSymlinkAsIs in options: createSymlink(expandSymlink(source), dest) @@ -246,15 +239,21 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, if status2 != 0: raiseOSError(osLastError(), $(source, dest)) else: # generic version of copyFile which works for any platform: - const bufSize = 8000 # better for memory manager var d, s: File - if not open(s, source):raiseOSError(osLastError(), source) + if not open(s, source): raiseOSError(osLastError(), source) if not open(d, dest, fmWrite): close(s) raiseOSError(osLastError(), dest) - var buf = alloc(bufSize) + + # Hints for kernel-level aggressive sequential low-fragmentation read-aheads: + # https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html + when defined(linux) or defined(osx): + discard posix_fadvise(getFileHandle(d), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL) + discard posix_fadvise(getFileHandle(s), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL) + + var buf = alloc(bufferSize) while true: - var bytesread = readBuffer(s, buf, bufSize) + var bytesread = readBuffer(s, buf, bufferSize) if bytesread > 0: var byteswritten = writeBuffer(d, buf, bytesread) if bytesread != byteswritten: @@ -262,13 +261,13 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, close(s) close(d) raiseOSError(osLastError(), dest) - if bytesread != bufSize: break + if bytesread != bufferSize: break dealloc(buf) close(s) flushFile(d) close(d) -proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) +proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}; bufferSize = 16_384) {.noWeirdTarget, since: (1,3,7).} = ## Copies a file `source` into directory `dir`, which must exist. ## @@ -276,12 +275,14 @@ proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) ## if `source` is a symlink, copies the file symlink points to. `options` is ## ignored on Windows: symlinks are skipped. ## + ## `copyFileToDir` allows to specify `bufferSize` to improve I/O performance. + ## ## See also: ## * `CopyFlag enum`_ ## * `copyFile proc`_ if dir.len == 0: # treating "" as "." is error prone raise newException(ValueError, "dest is empty") - copyFile(source, dir / source.lastPathPart, options) + copyFile(source, dir / source.lastPathPart, options, bufferSize) proc copyFileWithPermissions*(source, dest: string, @@ -333,14 +334,9 @@ when not declared(ENOENT) and not defined(windows): var ENOENT {.importc, header: "<errno.h>".}: cint when defined(windows) and not weirdTarget: - when useWinUnicode: - template deleteFile(file: untyped): untyped = deleteFileW(file) - template setFileAttributes(file, attrs: untyped): untyped = - setFileAttributesW(file, attrs) - else: - template deleteFile(file: untyped): untyped = deleteFileA(file) - template setFileAttributes(file, attrs: untyped): untyped = - setFileAttributesA(file, attrs) + template deleteFile(file: untyped): untyped = deleteFileW(file) + template setFileAttributes(file, attrs: untyped): untyped = + setFileAttributesW(file, attrs) proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = ## Removes the `file`. @@ -357,10 +353,7 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE ## * `moveFile proc`_ result = true when defined(windows): - when useWinUnicode: - let f = newWideCString(file) - else: - let f = file + let f = newWideCString(file) if deleteFile(f) == 0: result = false let err = getLastError() @@ -412,12 +405,12 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", if not tryMoveFSObject(source, dest, isDir = false): when defined(windows): - doAssert false + raiseAssert "unreachable" else: # Fallback to copy & del - copyFile(source, dest, {cfSymlinkAsIs}) + copyFileWithPermissions(source, dest, options={cfSymlinkAsIs}) try: removeFile(source) except: discard tryRemoveFile(dest) - raise \ No newline at end of file + raise diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 75c34ecf5..bc69ff725 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -1,7 +1,7 @@ include system/inclrtl import std/private/since -import strutils, pathnorm +import std/[strutils, pathnorm] import std/oserrors import oscommon @@ -10,14 +10,16 @@ export ReadDirEffect, WriteDirEffect when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] +## .. importdoc:: osappdirs.nim, osdirs.nim, osseps.nim, os.nim + const weirdTarget = defined(nimscript) or defined(js) when weirdTarget: discard elif defined(windows): - import winlean + import std/winlean elif defined(posix): - import posix, system/ansi_c + import std/posix, system/ansi_c else: {.error: "OS module not ported to your operating system!".} @@ -252,12 +254,12 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise result = path[0] != ':' elif defined(RISCOS): result = path[0] == '$' - elif defined(posix) or defined(js): - # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 - # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs + elif defined(posix): result = path[0] == '/' + elif defined(nodejs): + {.emit: [result," = require(\"path\").isAbsolute(",path.cstring,");"].} else: - doAssert false # if ever hits here, adapt as needed + raiseAssert "unreachable" # if ever hits here, adapt as needed when FileSystemCaseSensitive: template `!=?`(a, b: char): bool = a != b @@ -569,7 +571,7 @@ proc normExt(ext: string): string = proc searchExtPos*(path: string): int = ## Returns index of the `'.'` char in `path` if it signifies the beginning - ## of extension. Returns -1 otherwise. + ## of the file extension. Returns -1 otherwise. ## ## See also: ## * `splitFile proc`_ @@ -582,15 +584,28 @@ proc searchExtPos*(path: string): int = assert searchExtPos("c.nim") == 1 assert searchExtPos("a/b/c.nim") == 5 assert searchExtPos("a.b.c.nim") == 5 + assert searchExtPos(".nim") == -1 + assert searchExtPos("..nim") == -1 + assert searchExtPos("a..nim") == 2 - # BUGFIX: do not search until 0! .DS_Store is no file extension! + # Unless there is any char that is not `ExtSep` before last `ExtSep` in the file name, + # it is not a file extension. + const DirSeps = when doslikeFileSystem: {DirSep, AltSep, ':'} else: {DirSep, AltSep} result = -1 - for i in countdown(len(path)-1, 1): + var i = path.high + while i >= 1: if path[i] == ExtSep: + break + elif path[i] in DirSeps: + return -1 # do not skip over path + dec i + + for j in countdown(i - 1, 0): + if path[j] in DirSeps: + return -1 + elif path[j] != ExtSep: result = i break - elif path[i] in {DirSep, AltSep}: - break # do not skip over path proc splitFile*(path: string): tuple[dir, name, ext: string] {. noSideEffect, rtl, extern: "nos$1".} = @@ -748,9 +763,9 @@ proc cmpPaths*(pathA, pathB: string): int {. ## On a case-sensitive filesystem this is done ## case-sensitively otherwise case-insensitively. Returns: ## - ## | 0 if pathA == pathB - ## | < 0 if pathA < pathB - ## | > 0 if pathA > pathB + ## | `0` if pathA == pathB + ## | `< 0` if pathA < pathB + ## | `> 0` if pathA > pathB runnableExamples: when defined(macosx): assert cmpPaths("foo", "Foo") == 0 @@ -844,33 +859,20 @@ when not defined(nimscript): {.emit: "`ret` = process.cwd();".} return $ret elif defined(js): - doAssert false, "use -d:nodejs to have `getCurrentDir` defined" + raiseAssert "use -d:nodejs to have `getCurrentDir` defined" elif defined(windows): var bufsize = MAX_PATH.int32 - when useWinUnicode: - var res = newWideCString("", bufsize) - while true: - var L = getCurrentDirectoryW(bufsize, res) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - res = newWideCString("", L) - bufsize = L - else: - result = res$L - break - else: - result = newString(bufsize) - while true: - var L = getCurrentDirectoryA(bufsize, result) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - result = newString(L) - bufsize = L - else: - setLen(result, L) - break + var res = newWideCString(bufsize) + while true: + var L = getCurrentDirectoryW(bufsize, res) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + res = newWideCString(L) + bufsize = L + else: + result = res$L + break else: var bufsize = 1024 # should be enough result = newString(bufsize) diff --git a/lib/std/private/osseps.nim b/lib/std/private/osseps.nim index 1ea587e3c..f2d49d886 100644 --- a/lib/std/private/osseps.nim +++ b/lib/std/private/osseps.nim @@ -3,6 +3,8 @@ # Improved based on info in 'compiler/platform.nim' +## .. importdoc:: ospaths2.nim + const doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS) diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim index cb6287bde..c1760c42e 100644 --- a/lib/std/private/ossymlinks.nim +++ b/lib/std/private/ossymlinks.nim @@ -10,9 +10,9 @@ when defined(nimPreviewSlimSystem): when weirdTarget: discard elif defined(windows): - import winlean, times + import std/[winlean, times] elif defined(posix): - import posix + import std/posix else: {.error: "OS module not ported to your operating system!".} @@ -31,6 +31,7 @@ elif defined(js): else: {.pragma: noNimJs.} +## .. importdoc:: os.nim proc createSymlink*(src, dest: string) {.noWeirdTarget.} = ## Create a symbolic link at `dest` which points to the item specified @@ -47,14 +48,10 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} = const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2 # allows anyone with developer mode on to create a link let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - when useWinUnicode: - var wSrc = newWideCString(src) - var wDst = newWideCString(dest) - if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: - raiseOSError(osLastError(), $(src, dest)) - else: - if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0: - raiseOSError(osLastError(), $(src, dest)) + var wSrc = newWideCString(src) + var wDst = newWideCString(dest) + if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: + raiseOSError(osLastError(), $(src, dest)) else: if symlink(src, dest) != 0: raiseOSError(osLastError(), $(src, dest)) @@ -66,14 +63,16 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = ## ## See also: ## * `createSymlink proc`_ - when defined(windows): + when defined(windows) or defined(nintendoswitch): result = symlinkPath else: - result = newString(maxSymlinkLen) - var len = readlink(symlinkPath, result.cstring, maxSymlinkLen) - if len < 0: - raiseOSError(osLastError(), symlinkPath) - if len > maxSymlinkLen: - result = newString(len+1) - len = readlink(symlinkPath, result.cstring, len) - setLen(result, len) + var bufLen = 1024 + while true: + result = newString(bufLen) + let len = readlink(symlinkPath.cstring, result.cstring, bufLen) + if len < 0: + raiseOSError(osLastError(), symlinkPath) + if len < bufLen: + result.setLen(len) + break + bufLen = bufLen shl 1 diff --git a/lib/std/private/schubfach.nim b/lib/std/private/schubfach.nim index dad8363ba..b8c85d2bc 100644 --- a/lib/std/private/schubfach.nim +++ b/lib/std/private/schubfach.nim @@ -39,10 +39,10 @@ const exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1) signMask: BitsType = not (not BitsType(0) shr 1) -proc constructSingle(bits: BitsType): Single {.constructor.} = +proc constructSingle(bits: BitsType): Single = result.bits = bits -proc constructSingle(value: ValueType): Single {.constructor.} = +proc constructSingle(value: ValueType): Single = result.bits = cast[typeof(result.bits)](value) proc physicalSignificand(this: Single): BitsType {.noSideEffect.} = @@ -244,12 +244,12 @@ proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal ## ToChars ## ================================================================================================== -proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int32 {.inline.} = +proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int {.inline.} = var output = output var pos = pos - var tz: int32 = 0 + var tz = 0 ## number of trailing zeros removed. - var nd: int32 = 0 + var nd = 0 ## number of decimal digits processed. ## At most 9 digits remaining if output >= 10000: @@ -355,7 +355,7 @@ proc formatDigits[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint3 ## dE+123 or d.igitsE+123 decimalDigitsPosition = 1 var digitsEnd = pos + decimalDigitsPosition + numDigits - let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) + let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits) dec(digitsEnd, tz) dec(numDigits, tz) ## decimal_exponent += tz; // => decimal_point unchanged. diff --git a/lib/std/private/since.nim b/lib/std/private/since.nim index 5b22b6391..720120f11 100644 --- a/lib/std/private/since.nim +++ b/lib/std/private/since.nim @@ -1,5 +1,5 @@ ##[ -`since` is used to emulate older versions of nim stdlib with `--useVersion`, +`since` is used to emulate older versions of nim stdlib, see `tuse_version.nim`. If a symbol `foo` is added in version `(1,3,5)`, use `{.since: (1.3.5).}`, not @@ -15,19 +15,19 @@ The emulation cannot be 100% faithful and to avoid adding too much complexity, template since*(version: (int, int), body: untyped) {.dirty.} = ## Evaluates `body` if the ``(NimMajor, NimMinor)`` is greater than ## or equal to `version`. Usage: - ## - ## .. code-block:: Nim + ## ```Nim ## proc fun*() {.since: (1, 3).} ## since (1, 3): fun() + ## ``` when (NimMajor, NimMinor) >= version: body template since*(version: (int, int, int), body: untyped) {.dirty.} = ## Evaluates `body` if ``(NimMajor, NimMinor, NimPatch)`` is greater than ## or equal to `version`. Usage: - ## - ## .. code-block:: Nim + ## ```Nim ## proc fun*() {.since: (1, 3, 1).} ## since (1, 3, 1): fun() + ## ``` when (NimMajor, NimMinor, NimPatch) >= version: body diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim index 6a38cbfd2..f8c9236a5 100644 --- a/lib/std/private/strimpl.nim +++ b/lib/std/private/strimpl.nim @@ -93,7 +93,7 @@ func find*(s: cstring, sub: char, start: Natural = 0, last = 0): int = if L > 0: let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L)) if not found.isNil: - return cast[ByteAddress](found) -% cast[ByteAddress](s) + return cast[int](found) -% cast[int](s) return -1 func find*(s, sub: cstring, start: Natural = 0, last = 0): int = @@ -108,6 +108,6 @@ func find*(s, sub: cstring, start: Natural = 0, last = 0): int = if last == 0 and s.len > start: let found = c_strstr(cast[cstring](s[start].unsafeAddr), sub) if not found.isNil: - result = cast[ByteAddress](found) -% cast[ByteAddress](s) + result = cast[int](found) -% cast[int](s) else: result = -1 diff --git a/lib/std/private/syslocks.nim b/lib/std/private/syslocks.nim index ca8897dc2..e19ec2c04 100644 --- a/lib/std/private/syslocks.nim +++ b/lib/std/private/syslocks.nim @@ -16,7 +16,7 @@ when defined(windows): Handle = int SysLock* {.importc: "CRITICAL_SECTION", - header: "<windows.h>", final, pure.} = object # CRITICAL_SECTION in WinApi + header: "<windows.h>", final, pure, byref.} = object # CRITICAL_SECTION in WinApi DebugInfo: pointer LockCount: int32 RecursionCount: int32 @@ -24,7 +24,7 @@ when defined(windows): LockSemaphore: int SpinCount: int - SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>".} = object + SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>", byref.} = object thePtr {.importc: "Ptr".} : Handle proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection", @@ -46,7 +46,7 @@ when defined(windows): header: "<windows.h>".} ## Releases the lock `L`. - proc deinitSys*(L: var SysLock) {.importc: "DeleteCriticalSection", + proc deinitSys*(L: SysLock) {.importc: "DeleteCriticalSection", header: "<windows.h>".} proc initializeConditionVariable( @@ -68,7 +68,7 @@ when defined(windows): proc initSysCond*(cond: var SysCond) {.inline.} = initializeConditionVariable(cond) - proc deinitSysCond*(cond: var SysCond) {.inline.} = + proc deinitSysCond*(cond: SysCond) {.inline.} = discard proc waitSysCond*(cond: var SysCond, lock: var SysLock) = discard sleepConditionVariableCS(cond, lock, -1'i32) @@ -83,13 +83,13 @@ elif defined(genode): header: Header.} = object proc initSysLock*(L: var SysLock) = discard - proc deinitSys*(L: var SysLock) = discard + proc deinitSys*(L: SysLock) = discard proc acquireSys*(L: var SysLock) {.noSideEffect, importcpp.} proc tryAcquireSys*(L: var SysLock): bool {.noSideEffect, importcpp.} proc releaseSys*(L: var SysLock) {.noSideEffect, importcpp.} proc initSysCond*(L: var SysCond) = discard - proc deinitSysCond*(L: var SysCond) = discard + proc deinitSysCond*(L: SysCond) = discard proc waitSysCond*(cond: var SysCond, lock: var SysLock) {. noSideEffect, importcpp.} proc signalSysCond*(cond: var SysCond) {. @@ -101,7 +101,7 @@ else: type SysLockObj {.importc: "pthread_mutex_t", pure, final, header: """#include <sys/types.h> - #include <pthread.h>""".} = object + #include <pthread.h>""", byref.} = object when defined(linux) and defined(amd64): abi: array[40 div sizeof(clong), clong] @@ -113,7 +113,7 @@ else: SysCondObj {.importc: "pthread_cond_t", pure, final, header: """#include <sys/types.h> - #include <pthread.h>""".} = object + #include <pthread.h>""", byref.} = object when defined(linux) and defined(amd64): abi: array[48 div sizeof(clonglong), clonglong] @@ -127,7 +127,7 @@ else: proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} - proc deinitSysAux(L: var SysLockObj) {.noSideEffect, + proc deinitSysAux(L: SysLockObj) {.noSideEffect, importc: "pthread_mutex_destroy", header: "<pthread.h>".} proc acquireSysAux(L: var SysLockObj) {.noSideEffect, @@ -156,7 +156,7 @@ else: L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj)))) initSysLockAux(L[], attr) - proc deinitSys*(L: var SysLock) = + proc deinitSys*(L: SysLock) = deinitSysAux(L[]) c_free(L) @@ -173,7 +173,7 @@ else: template initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) = initSysLockAux(L, attr) - template deinitSys*(L: var SysLock) = + template deinitSys*(L: SysLock) = deinitSysAux(L) template acquireSys*(L: var SysLock) = acquireSysAux(L) @@ -193,7 +193,7 @@ else: # locks proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {. importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} - proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, + proc deinitSysCondAux(cond: SysCondObj) {.noSideEffect, importc: "pthread_cond_destroy", header: "<pthread.h>".} proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {. @@ -208,7 +208,7 @@ else: cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj)))) initSysCondAux(cond[], cond_attr) - proc deinitSysCond*(cond: var SysCond) = + proc deinitSysCond*(cond: SysCond) = deinitSysCondAux(cond[]) c_free(cond) @@ -221,7 +221,7 @@ else: else: template initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = initSysCondAux(cond, cond_attr) - template deinitSysCond*(cond: var SysCond) = + template deinitSysCond*(cond: SysCond) = deinitSysCondAux(cond) template waitSysCond*(cond: var SysCond, lock: var SysLock) = diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim index f0bcbcc74..f853572b5 100644 --- a/lib/std/private/underscored_calls.nim +++ b/lib/std/private/underscored_calls.nim @@ -10,7 +10,9 @@ ## This is an internal helper module. Do not use. -import macros +import std/macros + +proc underscoredCalls*(result, calls, arg0: NimNode) proc underscoredCall(n, arg0: NimNode): NimNode = proc underscorePos(n: NimNode): int = @@ -19,13 +21,19 @@ proc underscoredCall(n, arg0: NimNode): NimNode = return 0 if n.kind in nnkCallKinds: - result = copyNimNode(n) - result.add n[0] + if n[0].kind in {nnkIdent, nnkSym} and n[0].eqIdent("with"): + expectKind n[1], {nnkIdent, nnkSym} - let u = underscorePos(n) - for i in 1..u-1: result.add n[i] - result.add arg0 - for i in u+1..n.len-1: result.add n[i] + result = newStmtList() + underscoredCalls(result, n[2 .. ^1].newStmtList, newDotExpr(arg0, n[1])) + else: + result = copyNimNode(n) + result.add n[0] + + let u = underscorePos(n) + for i in 1..u-1: result.add n[i] + result.add arg0 + for i in u+1..n.len-1: result.add n[i] elif n.kind in {nnkAsgn, nnkExprEqExpr}: var field = n[0] if n[0].kind == nnkDotExpr and n[0][0].eqIdent("_"): diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim index 303889a40..66e199dfe 100644 --- a/lib/std/private/win_setenv.nim +++ b/lib/std/private/win_setenv.nim @@ -33,25 +33,25 @@ else: # same as winlean.setEnvironmentVariableA proc c_getenv(varname: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".} - proc c_wputenv(envstring: WideCString): cint {.importc: "_wputenv", header: "<stdlib.h>".} - proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".} + proc c_wputenv(envstring: ptr wchar_t): cint {.importc: "_wputenv", header: "<stdlib.h>".} + proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "<stdlib.h>".} var errno {.importc, header: "<errno.h>".}: cint var genviron {.importc: "_environ".}: ptr ptr char # xxx `ptr UncheckedArray[WideCString]` did not work - proc wcstombs(wcstr: ptr char, mbstr: WideCString, count: csize_t): csize_t {.importc, header: "<stdlib.h>".} + proc wcstombs(wcstr: ptr char, mbstr: ptr wchar_t, count: csize_t): csize_t {.importc, header: "<stdlib.h>".} # xxx cint vs errno_t? proc setEnvImpl*(name: string, value: string, overwrite: cint): cint = const EINVAL = cint(22) - let wideName = newWideCString(name) - if overwrite == 0 and c_wgetenv(wideName) != nil: + let wideName: WideCString = newWideCString(name) + if overwrite == 0 and c_wgetenv(cast[ptr wchar_t](wideName)) != nil: return 0 if value != "": - let envstring = name & "=" & value - let e = c_wputenv(newWideCString(envstring)) + let envstring: WideCString = newWideCString(name & "=" & value) + let e = c_wputenv(cast[ptr wchar_t](envstring)) if e != 0: errno = EINVAL return -1 @@ -62,19 +62,19 @@ else: SetEnvironmentVariableA doesn't update `_environ`, so we have to do these terrible things. ]# - let envstring = name & "= " - if c_wputenv(newWideCString(envstring)) != 0: + let envstring: WideCString = newWideCString(name & "= ") + if c_wputenv(cast[ptr wchar_t](envstring)) != 0: errno = EINVAL return -1 # Here lies the documentation we blatently ignore to make this work. - var s = c_wgetenv(wideName) + var s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName))) s[0] = Utf16Char('\0') #[ This would result in a double null termination, which normally signifies the end of the environment variable list, so we stick a completely empty environment variable into the list instead. ]# - s = c_wgetenv(wideName) + s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName))) s[1] = Utf16Char('=') #[ If genviron is null, the MBCS environment has not been initialized @@ -88,12 +88,12 @@ else: # in the current codepage. Skip updating MBCS environment in this case. # For some reason, second `wcstombs` can find non-convertible characters # that the first `wcstombs` cannot. - let requiredSizeS = wcstombs(nil, wideName, 0) + let requiredSizeS = wcstombs(nil, cast[ptr wchar_t](wideName), 0) if requiredSizeS != high(csize_t): let requiredSize = requiredSizeS.int var buf = newSeq[char](requiredSize + 1) let buf2 = buf[0].addr - if wcstombs(buf2, wideName, csize_t(requiredSize + 1)) != high(csize_t): + if wcstombs(buf2, cast[ptr wchar_t](wideName), csize_t(requiredSize + 1)) != high(csize_t): var ptrToEnv = c_getenv(cast[cstring](buf2)) ptrToEnv[0] = '\0' ptrToEnv = c_getenv(cast[cstring](buf2)) diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim index 4664d6dcc..8e7bc6a92 100644 --- a/lib/std/setutils.nim +++ b/lib/std/setutils.nim @@ -14,7 +14,7 @@ ## * `std/packedsets <packedsets.html>`_ ## * `std/sets <sets.html>`_ -import typetraits, macros +import std/[typetraits, macros] #[ type SetElement* = char|byte|bool|int16|uint16|enum|uint8|int8 diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 50175024c..213af4229 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -26,8 +26,11 @@ runnableExamples("-r:off"): b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C") assert a == b, "files don't match" -import strutils -from endians import bigEndian32, bigEndian64 + +{.deprecated: "use command `nimble install checksums` and import `checksums/sha1` instead".} + +import std/strutils +from std/endians import bigEndian32, bigEndian64 when defined(nimPreviewSlimSystem): import std/syncio @@ -281,4 +284,4 @@ proc `==`*(a, b: SecureHash): bool = proc isValidSha1Hash*(s: string): bool = ## Checks if a string is a valid sha1 hash sum. - s.len == 40 and allCharsInSet(s, HexDigits) + s.len == 40 and allCharsInSet(s, HexDigits) \ No newline at end of file diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim index 5c882858d..45e906795 100644 --- a/lib/std/socketstreams.nim +++ b/lib/std/socketstreams.nim @@ -31,39 +31,40 @@ ## Examples ## ======== ## -## .. code-block:: Nim -## import std/socketstreams +## ```Nim +## import std/socketstreams ## -## var -## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## stream = newReadSocketStream(socket) -## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST") -## echo stream.readLine() # Will call `recv` -## stream.setPosition(0) -## echo stream.readLine() # Will return the read line from the buffer -## stream.resetStream() # Buffer is now empty, position is 0 -## echo stream.readLine() # Will call `recv` again -## stream.close() # Closes the socket +## var +## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## stream = newReadSocketStream(socket) +## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST") +## echo stream.readLine() # Will call `recv` +## stream.setPosition(0) +## echo stream.readLine() # Will return the read line from the buffer +## stream.resetStream() # Buffer is now empty, position is 0 +## echo stream.readLine() # Will call `recv` again +## stream.close() # Closes the socket +## ``` ## -## .. code-block:: Nim +## ```Nim +## import std/socketstreams ## -## import std/socketstreams -## -## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## socket.connect("127.0.0.1", Port(12345)) -## var sendStream = newWriteSocketStream(socket) -## sendStream.write "NOM" -## sendStream.setPosition(1) -## echo sendStream.peekStr(2) # OM -## sendStream.write "I" -## sendStream.setPosition(0) -## echo sendStream.readStr(3) # NIM -## echo sendStream.getPosition() # 3 -## sendStream.flush() # This actually performs the writing to the socket -## sendStream.setPosition(1) -## sendStream.write "I" # Throws an error as we can't write into an already sent buffer - -import net, streams +## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## socket.connect("127.0.0.1", Port(12345)) +## var sendStream = newWriteSocketStream(socket) +## sendStream.write "NOM" +## sendStream.setPosition(1) +## echo sendStream.peekStr(2) # OM +## sendStream.write "I" +## sendStream.setPosition(0) +## echo sendStream.readStr(3) # NIM +## echo sendStream.getPosition() # 3 +## sendStream.flush() # This actually performs the writing to the socket +## sendStream.setPosition(1) +## sendStream.write "I" # Throws an error as we can't write into an already sent buffer +## ``` + +import std/[net, streams] type ReadSocketStream* = ref ReadSocketStreamObj @@ -146,7 +147,7 @@ proc wsFlush(s: Stream) = s.lastFlush = s.buf.len proc rsClose(s: Stream) = - {.cast(tags: []).}: + {.cast(raises: [IOError, OSError]), cast(tags: []).}: # todo fixme maybe do something? var s = ReadSocketStream(s) s.data.close() diff --git a/lib/std/staticos.nim b/lib/std/staticos.nim new file mode 100644 index 000000000..2617c6913 --- /dev/null +++ b/lib/std/staticos.nim @@ -0,0 +1,13 @@ +## This module implements path handling like os module but works at only compile-time. +## This module works even when cross compiling to OS that is not supported by os module. + +proc staticFileExists*(filename: string): bool {.compileTime.} = + ## Returns true if `filename` exists and is a regular file or symlink. + ## + ## Directories, device files, named pipes and sockets return false. + discard + +proc staticDirExists*(dir: string): bool {.compileTime.} = + ## Returns true if the directory `dir` exists. If `dir` is a file, false + ## is returned. Follows symlinks. + discard diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim index be1dd7a58..b2c36a4be 100644 --- a/lib/std/strbasics.nim +++ b/lib/std/strbasics.nim @@ -23,8 +23,8 @@ proc add*(x: var string, y: openArray[char]) = # Use `{.noalias.}` ? let n = x.len x.setLen n + y.len - # pending https://github.com/nim-lang/Nim/issues/14655#issuecomment-643671397 - # use x.setLen(n + y.len, isInit = false) + # pending #19727 + # setLen unnecessarily zeros memory var i = 0 while i < y.len: x[n + i] = y[i] diff --git a/lib/std/symlinks.nim b/lib/std/symlinks.nim index 9e77bbe2a..dbe908612 100644 --- a/lib/std/symlinks.nim +++ b/lib/std/symlinks.nim @@ -1,11 +1,12 @@ ## This module implements symlink (symbolic link) handling. -from paths import Path, ReadDirEffect +## .. importdoc:: os.nim -from std/private/ossymlinks import symlinkExists, createSymlink, expandSymlink +from std/paths import Path, ReadDirEffect +from std/private/ossymlinks import symlinkExists, createSymlink, expandSymlink -proc symlinkExists*(link: Path): bool {.inline, tags: [ReadDirEffect].} = +proc symlinkExists*(link: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} = ## Returns true if the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. result = symlinkExists(link.string) diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index 2b5ea6a3c..c34a025af 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -38,8 +38,8 @@ type ## at the end. If the file does not exist, it ## will be created. - FileHandle* = cint ## type that represents an OS file handle; this is - ## useful for low-level file access + FileHandle* = cint ## The type that represents an OS file handle; this is + ## useful for low-level file access. FileSeekPos* = enum ## Position relative to which seek should happen. # The values are ordered so that they match with stdio @@ -151,14 +151,11 @@ proc c_fprintf(f: File, frmt: cstring): cint {. proc c_fputc(c: char, f: File): cint {. importc: "fputc", header: "<stdio.h>".} -template sysFatal(exc, msg) = - raise newException(exc, msg) - proc raiseEIO(msg: string) {.noinline, noreturn.} = - sysFatal(IOError, msg) + raise newException(IOError, msg) proc raiseEOF() {.noinline, noreturn.} = - sysFatal(EOFError, "EOF reached") + raise newException(EOFError, "EOF reached") proc strerror(errnum: cint): cstring {.importc, header: "<string.h>".} @@ -246,7 +243,7 @@ when defined(windows): # machine. We also enable `setConsoleOutputCP(65001)` now by default. # But we cannot call printf directly as the string might contain \0. # So we have to loop over all the sections separated by potential \0s. - var i = c_fprintf(f, "%s", s) + var i = int c_fprintf(f, "%s", s) while i < s.len: if s[i] == '\0': let w = c_fputc('\0', f) @@ -323,7 +320,7 @@ elif defined(windows): const BufSize = 4000 -proc close*(f: File) {.tags: [], gcsafe.} = +proc close*(f: File) {.tags: [], gcsafe, sideEffect.} = ## Closes the file. if not f.isNil: discard c_fclose(f) @@ -359,12 +356,12 @@ proc getOsFileHandle*(f: File): FileHandle = when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(windows): proc setInheritable*(f: FileHandle, inheritable: bool): bool = - ## control whether a file handle can be inherited by child processes. Returns + ## Controls whether a file handle can be inherited by child processes. Returns ## `true` on success. This requires the OS file handle, which can be ## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_. ## ## This procedure is not guaranteed to be available for all platforms. Test for - ## availability with `declared() <system.html#declared,untyped>`. + ## availability with `declared() <system.html#declared,untyped>`_. when SupportIoctlInheritCtl: result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1 elif defined(freertos) or defined(zephyr): @@ -390,7 +387,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {. importc: "memchr", header: "<string.h>".} - when defined(windows) and not defined(useWinAnsi): + when defined(windows): proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer, nNumberOfCharsToRead: int32, lpNumberOfCharsRead: ptr int32, @@ -422,7 +419,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], if f.isatty: const numberOfCharsToRead = 2048 var numberOfCharsRead = 0'i32 - var buffer = newWideCString("", numberOfCharsToRead) + var buffer = newWideCString(numberOfCharsToRead) if readConsole(getOsFileHandle(f), addr(buffer[0]), numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0: var error = getLastError() @@ -480,15 +477,16 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], let m = c_memchr(addr line[pos], '\L'.ord, cast[csize_t](sp)) if m != nil: # \l found: Could be our own or the one by fgets, in any case, we're done - var last = cast[ByteAddress](m) - cast[ByteAddress](addr line[0]) + var last = cast[int](m) - cast[int](addr line[0]) if last > 0 and line[last-1] == '\c': line.setLen(last-1) return last > 1 or fgetsSuccess - # We have to distinguish between two possible cases: + elif last > 0 and line[last-1] == '\0': + # We have to distinguish among three possible cases: # \0\l\0 => line ending in a null character. # \0\l\l => last line without newline, null was put there by fgets. - elif last > 0 and line[last-1] == '\0': - if last < pos + sp - 1 and line[last+1] != '\0': + # \0\l => last line without newline, null was put there by fgets. + if last >= pos + sp - 1 or line[last+1] != '\0': # bug #21273 dec last line.setLen(last) return last > 0 or fgetsSuccess @@ -611,7 +609,7 @@ proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, # interface to the C procs: -when defined(windows) and not defined(useWinAnsi): +when defined(windows): when defined(cpp): proc wfopen(filename, mode: WideCString): pointer {. importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.} @@ -650,6 +648,9 @@ const "" else: "" + RawFormatOpen: array[FileMode, cstring] = [ + # used for open by FileHandle, which calls `fdopen` + cstring("rb"), "wb", "w+b", "r+b", "ab"] FormatOpen: array[FileMode, cstring] = [ cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag, "r+b" & NoInheritFlag, "ab" & NoInheritFlag @@ -751,7 +752,7 @@ proc open*(f: var File, filehandle: FileHandle, filehandle) else: filehandle if not setInheritable(oshandle, false): return false - f = c_fdopen(filehandle, FormatOpen[mode]) + f = c_fdopen(filehandle, RawFormatOpen[mode]) result = f != nil proc open*(filename: string, @@ -763,9 +764,9 @@ proc open*(filename: string, ## ## The file handle associated with the resulting `File` is not inheritable. if not open(result, filename, mode, bufSize): - sysFatal(IOError, "cannot open: " & filename) + raise newException(IOError, "cannot open: " & filename) -proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} = +proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign, sideEffect.} = ## Sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. if c_fseek(f, pos, cint(relativeTo)) != 0: @@ -851,7 +852,7 @@ proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} = finally: close(f) else: - sysFatal(IOError, "cannot open: " & filename) + raise newException(IOError, "cannot open: " & filename) proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} = ## Opens a file named `filename` for writing. Then writes the @@ -864,7 +865,7 @@ proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} = finally: close(f) else: - sysFatal(IOError, "cannot open: " & filename) + raise newException(IOError, "cannot open: " & filename) proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = ## Opens a file named `filename` for writing. Then writes the @@ -873,7 +874,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = var f: File = nil if open(f, filename, fmWrite): try: - f.writeBuffer(unsafeAddr content[0], content.len) + discard f.writeBuffer(unsafeAddr content[0], content.len) finally: close(f) else: @@ -894,7 +895,7 @@ proc readLines*(filename: string, n: Natural): seq[string] = finally: close(f) else: - sysFatal(IOError, "cannot open: " & filename) + raise newException(IOError, "cannot open: " & filename) template readLines*(filename: string): seq[ string] {.deprecated: "use readLines with two arguments".} = diff --git a/lib/std/sysatomics.nim b/lib/std/sysatomics.nim index b7ccb4092..2f203b3eb 100644 --- a/lib/std/sysatomics.nim +++ b/lib/std/sysatomics.nim @@ -15,7 +15,7 @@ when defined(nimPreviewSlimSystem): const hasThreadSupport = compileOption("threads") and not defined(nimscript) -const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) +const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) or defined(nintendoswitch) const someVcc = defined(vcc) or defined(clang_cl) type @@ -265,6 +265,7 @@ else: proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} = + ## Atomically increments the integer by some `x`. It returns the new value. when someGcc and hasThreadSupport: result = atomicAddFetch(memLoc.addr, x, ATOMIC_SEQ_CST) elif someVcc and hasThreadSupport: @@ -275,6 +276,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: result = memLoc proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} = + ## Atomically decrements the integer by some `x`. It returns the new value. when someGcc and hasThreadSupport: when declared(atomicSubFetch): result = atomicSubFetch(memLoc.addr, x, ATOMIC_SEQ_CST) @@ -361,7 +363,7 @@ elif someGcc or defined(tcc): elif defined(icl): proc cpuRelax* {.importc: "_mm_pause", header: "xmmintrin.h".} elif false: - from os import sleep + from std/os import sleep proc cpuRelax* {.inline.} = os.sleep(1) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index eeaa23d72..6f2c6b0c1 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -60,7 +60,7 @@ when not defined(js): import std/oserrors when defined(posix): - import posix + import std/posix when defined(nimPreviewSlimSystem): import std/assertions @@ -168,8 +168,10 @@ elif defined(windows): result = randomBytes(addr dest[0], size) elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): - # TODO using let, pending bootstrap >= 1.4.0 - var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong + when (NimMajor, NimMinor) >= (1, 4): + let SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong + else: + var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong const syscallHeader = """#include <unistd.h> #include <sys/syscall.h>""" @@ -190,12 +192,11 @@ elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): while result < size: let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int if readBytes == 0: - doAssert false + raiseAssert "unreachable" elif readBytes > 0: inc(result, readBytes) else: - if osLastError().int in {EINTR, EAGAIN}: - discard + if osLastError().cint in [EINTR, EAGAIN]: discard else: result = -1 break diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 055ddf144..7e59747f5 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -11,7 +11,6 @@ ## A `Task` should be only owned by a single Thread, it cannot be shared by threads. import std/[macros, isolation, typetraits] -import system/ansi_c when defined(nimPreviewSlimSystem): import std/assertions @@ -62,24 +61,33 @@ when compileOption("threads"): type Task* = object ## `Task` contains the callback and its arguments. - callback: proc (args: pointer) {.nimcall, gcsafe.} + callback: proc (args, res: pointer) {.nimcall, gcsafe.} args: pointer destroy: proc (args: pointer) {.nimcall, gcsafe.} proc `=copy`*(x: var Task, y: Task) {.error.} -proc `=destroy`*(t: var Task) {.inline, gcsafe.} = - ## Frees the resources allocated for a `Task`. - if t.args != nil: - if t.destroy != nil: - t.destroy(t.args) - c_free(t.args) - -proc invoke*(task: Task) {.inline, gcsafe.} = +const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) +when defined(nimAllowNonVarDestructor) and arcLike: + proc `=destroy`*(t: Task) {.inline, gcsafe.} = + ## Frees the resources allocated for a `Task`. + if t.args != nil: + if t.destroy != nil: + t.destroy(t.args) + deallocShared(t.args) +else: + proc `=destroy`*(t: var Task) {.inline, gcsafe.} = + ## Frees the resources allocated for a `Task`. + if t.args != nil: + if t.destroy != nil: + t.destroy(t.args) + deallocShared(t.args) + +proc invoke*(task: Task; res: pointer = nil) {.inline, gcsafe.} = ## Invokes the `task`. assert task.callback != nil - task.callback(task.args) + task.callback(task.args, res) template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr: NimNode) = # block: @@ -102,22 +110,38 @@ template addAllNode(assignParam: NimNode, procParam: NimNode) = tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0])) scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam) +proc analyseRootSym(s: NimNode): NimNode = + result = s + while true: + case result.kind + of nnkBracketExpr, nnkDerefExpr, nnkHiddenDeref, + nnkAddr, nnkHiddenAddr, + nnkObjDownConv, nnkObjUpConv: + result = result[0] + of nnkDotExpr, nnkCheckedFieldExpr, nnkHiddenStdConv, nnkHiddenSubConv: + result = result[1] + else: + break + macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task = ## Converts the call and its arguments to `Task`. - runnableExamples("--gc:orc"): + runnableExamples: proc hello(a: int) = echo a let b = toTask hello(13) assert b is Task - if getTypeInst(e).typeKind != ntyVoid: - error("'toTask' cannot accept a call with a return value", e) + let retType = getTypeInst(e) + let returnsVoid = retType.typeKind == ntyVoid + + let rootSym = analyseRootSym(e[0]) + expectKind rootSym, nnkSym when compileOption("threads"): - if not isGcSafe(e[0]): + if not isGcSafe(rootSym): error("'toTask' takes a GC safe call expression", e) - if hasClosure(e[0]): + if hasClosure(rootSym): error("closure call is not allowed", e) if e.len > 1: @@ -166,7 +190,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC # passing by static parameters # so we pass them directly instead of passing by scratchObj callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) - of nnkSym, nnkPtrTy: + of nnkSym, nnkPtrTy, nnkProcTy, nnkTupleConstr: addAllNode(param, e[i]) of nnkCharLit..nnkNilLit: callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) @@ -188,40 +212,43 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let scratchObjPtrType = quote do: - cast[ptr `scratchObjType`](c_calloc(csize_t 1, csize_t sizeof(`scratchObjType`))) - - let scratchLetSection = newLetStmt( - scratchIdent, - scratchObjPtrType - ) + cast[ptr `scratchObjType`](allocShared0(sizeof(`scratchObjType`))) - let scratchCheck = quote do: - if `scratchIdent`.isNil: - raise newException(OutOfMemDefect, "Could not allocate memory") + let scratchLetSection = newLetStmt(scratchIdent, scratchObjPtrType) var stmtList = newStmtList() stmtList.add(scratchObj) stmtList.add(scratchLetSection) - stmtList.add(scratchCheck) stmtList.add(nnkBlockStmt.newTree(newEmptyNode(), newStmtList(scratchAssignList))) var functionStmtList = newStmtList() let funcCall = newCall(e[0], callNode) functionStmtList.add tempAssignList - functionStmtList.add funcCall - let funcName = genSym(nskProc, e[0].strVal) + let funcName = genSym(nskProc, rootSym.strVal) let destroyName = genSym(nskProc, "destroyScratch") let objTemp2 = genSym(ident = "obj") let tempNode = quote("@") do: `=destroy`(@objTemp2[]) + var funcDecl: NimNode + if returnsVoid: + funcDecl = quote do: + proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} = + let `objTemp` = cast[ptr `scratchObjType`](args) + `functionStmtList` + `funcCall` + else: + funcDecl = quote do: + proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} = + let `objTemp` = cast[ptr `scratchObjType`](args) + `functionStmtList` + cast[ptr `retType`](res)[] = `funcCall` + result = quote do: `stmtList` - proc `funcName`(args: pointer) {.gcsafe, nimcall.} = - let `objTemp` = cast[ptr `scratchObjType`](args) - `functionStmtList` + `funcDecl` proc `destroyName`(args: pointer) {.gcsafe, nimcall.} = let `objTemp2` = cast[ptr `scratchObjType`](args) @@ -230,18 +257,26 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`) else: let funcCall = newCall(e[0]) - let funcName = genSym(nskProc, e[0].strVal) + let funcName = genSym(nskProc, rootSym.strVal) - result = quote do: - proc `funcName`(args: pointer) {.gcsafe, nimcall.} = - `funcCall` + if returnsVoid: + result = quote do: + proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} = + `funcCall` + + Task(callback: `funcName`, args: nil) + else: + result = quote do: + proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} = + cast[ptr `retType`](res)[] = `funcCall` + + Task(callback: `funcName`, args: nil) - Task(callback: `funcName`, args: nil) when defined(nimTasksDebug): echo result.repr -runnableExamples("--gc:orc"): +runnableExamples: block: var num = 0 proc hello(a: int) = inc num, a diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index 1160aaaad..539305bde 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -17,7 +17,7 @@ See also: * `mkstemp` (posix), refs https://man7.org/linux/man-pages/man3/mkstemp.3.html ]# -import os, random +import std / [os, random] when defined(nimPreviewSlimSystem): import std/syncio @@ -29,7 +29,7 @@ const when defined(windows): - import winlean + import std/winlean when defined(nimPreviewSlimSystem): import std/widestrs @@ -46,7 +46,7 @@ when defined(windows): proc close_osfandle(fd: cint): cint {. importc: "_close", header: "<io.h>".} else: - import posix + import std/posix proc c_fdopen( filehandle: cint, diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim index 7fb6e6d46..de051b135 100644 --- a/lib/std/time_t.nim +++ b/lib/std/time_t.nim @@ -14,10 +14,10 @@ when defined(nimdoc): ## Wrapper for `time_t`. On posix, this is an alias to `posix.Time`. elif defined(windows): when defined(i386) and defined(gcc): - type Time* {.importc: "time_t", header: "<time.h>".} = distinct int32 + type Time* {.importc: "time_t", header: "<time.h>".} = distinct clong else: # newest version of Visual C++ defines time_t to be of 64 bits type Time* {.importc: "time_t", header: "<time.h>".} = distinct int64 elif defined(posix): - import posix + import std/posix export posix.Time \ No newline at end of file diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 53b18d01e..7b0b81968 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -7,38 +7,74 @@ # distribution, for details about the copyright. # -## Thread support for Nim. -## -## Nim's memory model for threads is quite different from other common -## programming languages (C, Pascal): Each thread has its own -## (garbage collected) heap and sharing of memory is restricted. This helps -## to prevent race conditions and improves efficiency. See `the manual for -## details of this memory model <manual.html#threads>`_. -## -## Examples -## ======== -## -## .. code-block:: Nim -## -## import std/locks -## -## var -## thr: array[0..4, Thread[tuple[a,b: int]]] -## L: Lock -## -## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = -## for i in interval.a..interval.b: -## acquire(L) # lock stdout -## echo i -## release(L) -## -## initLock(L) -## -## for i in 0..high(thr): -## createThread(thr[i], threadFunc, (i*10, i*10+5)) -## joinThreads(thr) -## -## deinitLock(L) +##[ +Thread support for Nim. Threads allow multiple functions to execute concurrently. + +In Nim, threads are a low-level construct and using a library like `malebolgia`, `taskpools` or `weave` is recommended. + +When creating a thread, you can pass arguments to it. As Nim's garbage collector does not use atomic references, sharing +`ref` and other variables managed by the garbage collector between threads is not supported. +Use global variables to do so, or pointers. + +Memory allocated using [`sharedAlloc`](./system.html#allocShared.t%2CNatural) can be used and shared between threads. + +To communicate between threads, consider using [channels](./system.html#Channel) + +Examples +======== + +```Nim +import std/locks + +var + thr: array[0..4, Thread[tuple[a,b: int]]] + L: Lock + +proc threadFunc(interval: tuple[a,b: int]) {.thread.} = + for i in interval.a..interval.b: + acquire(L) # lock stdout + echo i + release(L) + +initLock(L) + +for i in 0..high(thr): + createThread(thr[i], threadFunc, (i*10, i*10+5)) +joinThreads(thr) + +deinitLock(L) +``` + +When using a memory management strategy that supports shared heaps like `arc` or `boehm`, +you can pass pointer to threads and share memory between them, but the memory must outlive the thread. +The default memory management strategy, `orc`, supports this. +The example below is **not valid** for memory management strategies that use local heaps like `refc`! + +```Nim +import locks + +var l: Lock + +proc threadFunc(obj: ptr seq[int]) {.thread.} = + withLock l: + for i in 0..<100: + obj[].add(obj[].len * obj[].len) + +proc threadHandler() = + var thr: array[0..4, Thread[ptr seq[int]]] + var s = newSeq[int]() + + for i in 0..high(thr): + createThread(thr[i], threadFunc, s.addr) + joinThreads(thr) + echo s + +initLock(l) +threadHandler() +deinitLock(l) +``` +]## + import std/private/[threadtypes] export Thread @@ -51,8 +87,11 @@ when defined(nimPreviewSlimSystem): when defined(genode): import genode/env +when hostOS == "any": + {.error: "Threads not implemented for os:any. Please compile with --threads:off.".} -when hasAllocStack or defined(zephyr) or defined(freertos) or defined(cpu16) or defined(cpu8): +when hasAllocStack or defined(zephyr) or defined(freertos) or defined(nuttx) or + defined(cpu16) or defined(cpu8): const nimThreadStackSize {.intdefine.} = 8192 nimThreadStackGuard {.intdefine.} = 128 diff --git a/lib/std/varints.nim b/lib/std/varints.nim index 0d18b9069..32fe2fffb 100644 --- a/lib/std/varints.nim +++ b/lib/std/varints.nim @@ -82,29 +82,29 @@ proc writeVu64*(z: var openArray[byte], x: uint64): int = z[3] = cast[uint8](y) return 4 z[0] = 251 - varintWrite32(toOpenArray(z, 1, z.high-1), y) + varintWrite32(toOpenArray(z, 1, 4), y) return 5 if w <= 255: z[0] = 252 z[1] = cast[uint8](w) - varintWrite32(toOpenArray(z, 2, z.high-2), y) + varintWrite32(toOpenArray(z, 2, 5), y) return 6 if w <= 65535: z[0] = 253 z[1] = cast[uint8](w shr 8) z[2] = cast[uint8](w) - varintWrite32(toOpenArray(z, 3, z.high-3), y) + varintWrite32(toOpenArray(z, 3, 6), y) return 7 if w <= 16777215: z[0] = 254 z[1] = cast[uint8](w shr 16) z[2] = cast[uint8](w shr 8) z[3] = cast[uint8](w) - varintWrite32(toOpenArray(z, 4, z.high-4), y) + varintWrite32(toOpenArray(z, 4, 7), y) return 8 z[0] = 255 - varintWrite32(toOpenArray(z, 1, z.high-1), w) - varintWrite32(toOpenArray(z, 5, z.high-5), y) + varintWrite32(toOpenArray(z, 1, 4), w) + varintWrite32(toOpenArray(z, 5, 8), y) return 9 proc sar(a, b: int64): int64 = diff --git a/lib/std/widestrs.nim b/lib/std/widestrs.nim index 8973579e1..2ddf80d14 100644 --- a/lib/std/widestrs.nim +++ b/lib/std/widestrs.nim @@ -25,12 +25,21 @@ when not (defined(cpu16) or defined(cpu8)): bytes: int data: WideCString - proc `=destroy`(a: var WideCStringObj) = - if a.data != nil: - when compileOption("threads"): - deallocShared(a.data) - else: - dealloc(a.data) + const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) + when defined(nimAllowNonVarDestructor) and arcLike: + proc `=destroy`(a: WideCStringObj) = + if a.data != nil: + when compileOption("threads"): + deallocShared(a.data) + else: + dealloc(a.data) + else: + proc `=destroy`(a: var WideCStringObj) = + if a.data != nil: + when compileOption("threads"): + deallocShared(a.data) + else: + dealloc(a.data) proc `=copy`(a: var WideCStringObj; b: WideCStringObj) {.error.} @@ -146,6 +155,7 @@ when not (defined(cpu16) or defined(cpu8)): createWide(result, size * 2 + 2) proc newWideCString*(source: cstring, L: int): WideCStringObj = + ## Warning:: `source` needs to be preallocated with the length `L` createWide(result, L * 2 + 2) var d = 0 for ch in runes(source, L): diff --git a/lib/std/with.nim b/lib/std/with.nim index c7338b4e4..c2eaa4bef 100644 --- a/lib/std/with.nim +++ b/lib/std/with.nim @@ -14,7 +14,7 @@ ## ## **Since:** version 1.2. -import macros, private / underscored_calls +import std/[macros, private / underscored_calls] macro with*(arg: typed; calls: varargs[untyped]): untyped = ## This macro provides `chaining`:idx: of function calls. @@ -35,5 +35,14 @@ macro with*(arg: typed; calls: varargs[untyped]): untyped = -= 5 doAssert a == 43 + # Nesting works for object types too! + var foo = (bar: 1, qux: (baz: 2)) + with foo: + bar = 2 + with qux: + baz = 3 + doAssert foo.bar == 2 + doAssert foo.qux.baz == 3 + result = newNimNode(nnkStmtList, arg) underscoredCalls(result, calls, arg) diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim index 7dcfc7f59..9333f880b 100644 --- a/lib/std/wordwrap.nim +++ b/lib/std/wordwrap.nim @@ -9,7 +9,7 @@ ## This module contains an algorithm to wordwrap a Unicode string. -import strutils, unicode +import std/[strutils, unicode] proc olen(s: string; start, lastExclusive: int): int = var i = start diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim index 235638134..0b75c270e 100644 --- a/lib/std/wrapnils.nim +++ b/lib/std/wrapnils.nim @@ -13,7 +13,7 @@ consider handling indexing operations, eg: doAssert ?.default(seq[int])[3] == default(int) ]# -import macros +import std/macros runnableExamples: type Foo = ref object @@ -122,7 +122,7 @@ macro `?.`*(a: typed): auto = `lhs` # the code below is not needed for `?.` -from options import Option, isSome, get, option, unsafeGet, UnpackDefect +from std/options import Option, isSome, get, option, unsafeGet, UnpackDefect macro `??.`*(a: typed): Option = ## Same as `?.` but returns an `Option`. |