mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 02:43:41 +00:00
Merge branch 'devel' into pr_remove_macros
This commit is contained in:
@@ -93,6 +93,8 @@ type
|
||||
nnkFuncDef,
|
||||
nnkTupleConstr,
|
||||
nnkError, ## erroneous AST node
|
||||
nnkModuleRef, nnkReplayAction, nnkNilRodNode ## internal IC nodes
|
||||
nnkOpenSym
|
||||
|
||||
NimNodeKinds* = set[NimNodeKind]
|
||||
NimTypeKind* = enum # some types are no longer used, see ast.nim
|
||||
@@ -227,7 +229,7 @@ when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform)
|
||||
## note that code transformations are implementation dependent and subject to change.
|
||||
## See an example in `tests/macros/tmacros_various.nim`.
|
||||
|
||||
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
|
||||
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect, deprecated.}
|
||||
## Accepts a node of kind `nnkSym` and returns its owner's symbol.
|
||||
## The meaning of 'owner' depends on `sym`'s `NimSymKind` and declaration
|
||||
## context. For top level declarations this is an `nskModule` symbol,
|
||||
@@ -1329,7 +1331,7 @@ proc `$`*(node: NimNode): string =
|
||||
result = node.basename.strVal & "*"
|
||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
|
||||
result = node.strVal
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice:
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
|
||||
result = $node[0]
|
||||
of nnkAccQuoted:
|
||||
result = ""
|
||||
|
||||
@@ -129,7 +129,9 @@ when not defined(gcDestructors):
|
||||
else:
|
||||
proc nimNewObj(size, align: int): pointer {.importCompilerProc.}
|
||||
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.}
|
||||
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
|
||||
proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
|
||||
importCompilerProc.}
|
||||
proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {.
|
||||
importCompilerProc.}
|
||||
|
||||
template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b)
|
||||
@@ -221,7 +223,8 @@ proc extendSeq*(x: Any) =
|
||||
var s = cast[ptr NimSeqV2Reimpl](x.value)
|
||||
let elem = x.rawType.base
|
||||
if s.p == nil or s.p.cap < s.len+1:
|
||||
s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align))
|
||||
s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAddUninit(s.len, s.p, 1, elem.size, elem.align))
|
||||
zeroNewElements(s.len, s.p, 1, elem.size, elem.align)
|
||||
inc s.len
|
||||
else:
|
||||
var y = cast[ptr PGenSeq](x.value)[]
|
||||
|
||||
@@ -243,7 +243,7 @@ since (1, 5, 1):
|
||||
else:
|
||||
type A = impl(onSuccess(default(T)))
|
||||
var ret: A
|
||||
{.emit: "`ret` = `future`.then(`onSuccess`, `onReject`)".}
|
||||
{.emit: "`ret` = `future`.then(`onSuccess`, `onReject`);".}
|
||||
return ret
|
||||
|
||||
proc catch*[T](future: Future[T], onReject: OnReject): Future[void] =
|
||||
@@ -266,4 +266,4 @@ since (1, 5, 1):
|
||||
|
||||
discard main()
|
||||
|
||||
{.emit: "`result` = `future`.catch(`onReject`)".}
|
||||
{.emit: "`result` = `future`.catch(`onReject`);".}
|
||||
|
||||
@@ -14,6 +14,7 @@ __GNUC__
|
||||
__TINYC__
|
||||
__clang__
|
||||
__AVR__
|
||||
__arm__
|
||||
__EMSCRIPTEN__
|
||||
*/
|
||||
|
||||
@@ -272,11 +273,15 @@ __EMSCRIPTEN__
|
||||
#elif defined(__cplusplus)
|
||||
#define NIM_STATIC_ASSERT(x, msg) static_assert((x), msg)
|
||||
#else
|
||||
#define NIM_STATIC_ASSERT(x, msg) typedef int NIM_STATIC_ASSERT_AUX[(x) ? 1 : -1];
|
||||
#define _NIM_STATIC_ASSERT_FINAL(x, append_name) typedef int NIM_STATIC_ASSERT_AUX ## append_name[(x) ? 1 : -1];
|
||||
#define _NIM_STATIC_ASSERT_STAGE_3(x, line) _NIM_STATIC_ASSERT_FINAL(x, _AT_LINE_##line)
|
||||
#define _NIM_STATIC_ASSERT_STAGE_2(x, line) _NIM_STATIC_ASSERT_STAGE_3(x, line)
|
||||
#define NIM_STATIC_ASSERT(x, msg) _NIM_STATIC_ASSERT_STAGE_2(x,__LINE__)
|
||||
// On failure, your C compiler will say something like:
|
||||
// "error: 'NIM_STATIC_ASSERT_AUX' declared as an array with a negative size"
|
||||
// we could use a better fallback to also show line number, using:
|
||||
// http://www.pixelbeat.org/programming/gcc/static_assert.html
|
||||
// "error: 'NIM_STATIC_ASSERT_AUX_AT_LINE_XXX' declared as an array with a negative size"
|
||||
// Adding the line number helps to avoid redefinitions which are not allowed in
|
||||
// old GCC versions, however the order of evaluation for __LINE__ is a little tricky,
|
||||
// hence all the helper macros. See https://stackoverflow.com/a/3385694 for more info.
|
||||
#endif
|
||||
|
||||
/* C99 compiler? */
|
||||
@@ -473,7 +478,7 @@ typedef char* NCSTRING;
|
||||
/* declared size of a sequence/variable length array: */
|
||||
#if defined(__cplusplus) && defined(__clang__)
|
||||
# define SEQ_DECL_SIZE 1
|
||||
#elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
|
||||
#elif defined(__GNUC__) || defined(_MSC_VER)
|
||||
# define SEQ_DECL_SIZE /* empty is correct! */
|
||||
#else
|
||||
# define SEQ_DECL_SIZE 1000000
|
||||
@@ -580,9 +585,16 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "P
|
||||
#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||
|
||||
#if NIM_INTBITS == 32
|
||||
#define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
|
||||
#if defined(__arm__) && defined(__GNUC__)
|
||||
/* arm-none-eabi-gcc targets defines int32_t as long int */
|
||||
#define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res)
|
||||
#else
|
||||
#define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
|
||||
#endif
|
||||
#else
|
||||
/* map it to the 'long long' variant */
|
||||
#define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
runnableExamples:
|
||||
from std/math import almostEqual, sqrt
|
||||
|
||||
func almostEqual(a, b: Complex): bool =
|
||||
almostEqual(a.re, b.re) and almostEqual(a.im, b.im)
|
||||
|
||||
let
|
||||
z1 = complex(1.0, 2.0)
|
||||
z2 = complex(3.0, -4.0)
|
||||
@@ -412,6 +409,24 @@ func rect*[T](r, phi: T): Complex[T] =
|
||||
## * `polar func<#polar,Complex[T]>`_ for the inverse operation
|
||||
complex(r * cos(phi), r * sin(phi))
|
||||
|
||||
func almostEqual*[T: SomeFloat](x, y: Complex[T]; unitsInLastPlace: Natural = 4): bool =
|
||||
## Checks if two complex values are almost equal, using the
|
||||
## [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon).
|
||||
##
|
||||
## Two complex values are considered almost equal if their real and imaginary
|
||||
## components are almost equal.
|
||||
##
|
||||
## `unitsInLastPlace` is the max number of
|
||||
## [units in the last place](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
|
||||
## difference tolerated when comparing two numbers. The larger the value, the
|
||||
## more error is allowed. A `0` value means that two numbers must be exactly the
|
||||
## same to be considered equal.
|
||||
##
|
||||
## The machine epsilon has to be scaled to the magnitude of the values used
|
||||
## and multiplied by the desired precision in ULPs unless the difference is
|
||||
## subnormal.
|
||||
almostEqual(x.re, y.re, unitsInLastPlace = unitsInLastPlace) and
|
||||
almostEqual(x.im, y.im, unitsInLastPlace = unitsInLastPlace)
|
||||
|
||||
func `$`*(z: Complex): string =
|
||||
## Returns `z`'s string representation as `"(re, im)"`.
|
||||
|
||||
@@ -15,75 +15,96 @@ runnableExamples:
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
when defined(posix) and not (defined(macosx) or defined(bsd)):
|
||||
import std/posix
|
||||
when defined(js):
|
||||
import std/jsffi
|
||||
proc countProcessorsImpl(): int =
|
||||
when defined(nodejs):
|
||||
let jsOs = require("os")
|
||||
let jsObj = jsOs.cpus().length
|
||||
else:
|
||||
# `navigator.hardwareConcurrency`
|
||||
# works on browser as well as deno.
|
||||
let navigator{.importcpp.}: JsObject
|
||||
let jsObj = navigator.hardwareConcurrency
|
||||
result = jsObj.to int
|
||||
else:
|
||||
when defined(posix) and not (defined(macosx) or defined(bsd)):
|
||||
import std/posix
|
||||
|
||||
when defined(windows):
|
||||
import std/private/win_getsysteminfo
|
||||
when defined(windows):
|
||||
import std/private/win_getsysteminfo
|
||||
|
||||
when defined(freebsd) or defined(macosx):
|
||||
{.emit: "#include <sys/types.h>".}
|
||||
when defined(freebsd) or defined(macosx):
|
||||
{.emit: "#include <sys/types.h>".}
|
||||
|
||||
when defined(openbsd) or defined(netbsd):
|
||||
{.emit: "#include <sys/param.h>".}
|
||||
when defined(openbsd) or defined(netbsd):
|
||||
{.emit: "#include <sys/param.h>".}
|
||||
|
||||
when defined(macosx) or defined(bsd):
|
||||
# we HAVE to emit param.h before sysctl.h so we cannot use .header here
|
||||
# either. The amount of archaic bullshit in Poonix based OSes is just insane.
|
||||
{.emit: "#include <sys/sysctl.h>".}
|
||||
const
|
||||
CTL_HW = 6
|
||||
HW_AVAILCPU = 25
|
||||
HW_NCPU = 3
|
||||
proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer,
|
||||
a: var csize_t, b: pointer, c: csize_t): cint {.
|
||||
importc: "sysctl", nodecl.}
|
||||
when defined(macosx) or defined(bsd):
|
||||
# we HAVE to emit param.h before sysctl.h so we cannot use .header here
|
||||
# either. The amount of archaic bullshit in Poonix based OSes is just insane.
|
||||
{.emit: "#include <sys/sysctl.h>".}
|
||||
{.push nodecl.}
|
||||
when defined(macosx):
|
||||
proc sysctlbyname(name: cstring,
|
||||
oldp: pointer, oldlenp: var csize_t,
|
||||
newp: pointer, newlen: csize_t): cint {.importc.}
|
||||
let
|
||||
CTL_HW{.importc.}: cint
|
||||
HW_NCPU{.importc.}: cint
|
||||
proc sysctl[I: static[int]](name: var array[I, cint], namelen: cuint,
|
||||
oldp: pointer, oldlenp: var csize_t,
|
||||
newp: pointer, newlen: csize_t): cint {.importc.}
|
||||
{.pop.}
|
||||
|
||||
when defined(genode):
|
||||
import genode/env
|
||||
when defined(genode):
|
||||
import genode/env
|
||||
|
||||
proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
|
||||
importcpp: "@->cpu().affinity_space().total()".}
|
||||
proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
|
||||
importcpp: "@->cpu().affinity_space().total()".}
|
||||
|
||||
when defined(haiku):
|
||||
type
|
||||
SystemInfo {.importc: "system_info", header: "<OS.h>".} = object
|
||||
cpuCount {.importc: "cpu_count".}: uint32
|
||||
|
||||
proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info",
|
||||
header: "<OS.h>".}
|
||||
|
||||
proc countProcessorsImpl(): int {.inline.} =
|
||||
when defined(windows):
|
||||
var
|
||||
si: SystemInfo
|
||||
getSystemInfo(addr si)
|
||||
result = int(si.dwNumberOfProcessors)
|
||||
elif defined(macosx) or defined(bsd):
|
||||
let dest = addr result
|
||||
var len = sizeof(result).csize_t
|
||||
when defined(macosx):
|
||||
# alias of "hw.activecpu"
|
||||
if sysctlbyname("hw.logicalcpu", dest, len, nil, 0) == 0:
|
||||
return
|
||||
var mib = [CTL_HW, HW_NCPU]
|
||||
if sysctl(mib, 2, dest, len, nil, 0) == 0:
|
||||
return
|
||||
elif defined(hpux):
|
||||
result = mpctl(MPC_GETNUMSPUS, nil, nil)
|
||||
elif defined(irix):
|
||||
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
|
||||
result = sysconf(SC_NPROC_ONLN)
|
||||
elif defined(genode):
|
||||
result = runtimeEnv.affinitySpaceTotal().int
|
||||
elif defined(haiku):
|
||||
var sysinfo: SystemInfo
|
||||
if getSystemInfo(addr sysinfo) == 0:
|
||||
result = sysinfo.cpuCount.int
|
||||
else:
|
||||
result = sysconf(SC_NPROCESSORS_ONLN)
|
||||
if result < 0: result = 0
|
||||
|
||||
when defined(haiku):
|
||||
type
|
||||
SystemInfo {.importc: "system_info", header: "<OS.h>".} = object
|
||||
cpuCount {.importc: "cpu_count".}: uint32
|
||||
|
||||
proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info",
|
||||
header: "<OS.h>".}
|
||||
|
||||
proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
|
||||
## Returns the number of the processors/cores the machine has.
|
||||
## Returns 0 if it cannot be detected.
|
||||
when defined(windows):
|
||||
var
|
||||
si: SystemInfo
|
||||
getSystemInfo(addr si)
|
||||
result = int(si.dwNumberOfProcessors)
|
||||
elif defined(macosx) or defined(bsd):
|
||||
var
|
||||
mib: array[0..3, cint]
|
||||
numCPU: int
|
||||
mib[0] = CTL_HW
|
||||
mib[1] = HW_AVAILCPU
|
||||
var len = sizeof(numCPU).csize_t
|
||||
discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
|
||||
if numCPU < 1:
|
||||
mib[1] = HW_NCPU
|
||||
discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
|
||||
result = numCPU
|
||||
elif defined(hpux):
|
||||
result = mpctl(MPC_GETNUMSPUS, nil, nil)
|
||||
elif defined(irix):
|
||||
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
|
||||
result = sysconf(SC_NPROC_ONLN)
|
||||
elif defined(genode):
|
||||
result = runtimeEnv.affinitySpaceTotal().int
|
||||
elif defined(haiku):
|
||||
var sysinfo: SystemInfo
|
||||
if getSystemInfo(addr sysinfo) == 0:
|
||||
result = sysinfo.cpuCount.int
|
||||
else:
|
||||
result = sysconf(SC_NPROCESSORS_ONLN)
|
||||
if result <= 0: result = 0
|
||||
countProcessorsImpl()
|
||||
|
||||
@@ -127,6 +127,7 @@ type
|
||||
|
||||
BSD
|
||||
FreeBSD
|
||||
NetBSD
|
||||
OpenBSD
|
||||
DragonFlyBSD
|
||||
|
||||
@@ -168,7 +169,7 @@ proc detectOsImpl(d: Distribution): bool =
|
||||
else:
|
||||
when defined(bsd):
|
||||
case d
|
||||
of Distribution.FreeBSD, Distribution.OpenBSD:
|
||||
of Distribution.FreeBSD, Distribution.NetBSD, Distribution.OpenBSD:
|
||||
result = $d in uname()
|
||||
else:
|
||||
result = false
|
||||
@@ -251,7 +252,7 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
|
||||
result = ("nix-env -i " & p, false)
|
||||
elif detectOs(Solaris) or detectOs(FreeBSD):
|
||||
result = ("pkg install " & p, true)
|
||||
elif detectOs(OpenBSD):
|
||||
elif detectOs(NetBSD) or detectOs(OpenBSD):
|
||||
result = ("pkg_add " & p, true)
|
||||
elif detectOs(PCLinuxOS):
|
||||
result = ("rpm -ivh " & p, true)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
## The types, vars and procs are bindings for the C standard library
|
||||
## [<fenv.h>](https://en.cppreference.com/w/c/numeric/fenv) header.
|
||||
|
||||
when defined(posix) and not defined(genode):
|
||||
when defined(posix) and not defined(genode) and not defined(macosx):
|
||||
{.passl: "-lm".}
|
||||
|
||||
var
|
||||
|
||||
@@ -190,7 +190,7 @@ proc hashData*(data: pointer, size: int): Hash =
|
||||
var h: Hash = 0
|
||||
when defined(js):
|
||||
var p: cstring
|
||||
{.emit: """`p` = `Data`""".}
|
||||
{.emit: """`p` = `Data`;""".}
|
||||
else:
|
||||
var p = cast[cstring](data)
|
||||
var i = 0
|
||||
@@ -379,6 +379,158 @@ proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash =
|
||||
proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash =
|
||||
raiseAssert "implementation override in compiler/vmops.nim"
|
||||
|
||||
const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses
|
||||
const k1 = 0xb492b66fbe98f273u64
|
||||
const k2 = 0x9ae16a3b2f90404fu64
|
||||
|
||||
proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
|
||||
uint32(s[o + 1]) shl 8 or uint32(s[o + 0])
|
||||
|
||||
proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
|
||||
uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
|
||||
uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
|
||||
uint64(s[o + 1]) shl 8 or uint64(s[o + 0])
|
||||
|
||||
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
when nimvm: result = load4e(s, o)
|
||||
else:
|
||||
when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load4e(s, o)
|
||||
|
||||
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
when nimvm: result = load8e(s, o)
|
||||
else:
|
||||
when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load8e(s, o)
|
||||
|
||||
proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
|
||||
|
||||
proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47)
|
||||
|
||||
proc rotR(v: uint64; bits: cint): uint64 {.inline.} =
|
||||
(v shr bits) or (v shl (64 - bits))
|
||||
|
||||
proc len16(u: uint64; v: uint64; mul: uint64): uint64 {.inline.} =
|
||||
var a = (u xor v)*mul
|
||||
a = a xor (a shr 47)
|
||||
var b = (v xor a)*mul
|
||||
b = b xor (b shr 47)
|
||||
b*mul
|
||||
|
||||
proc len0_16(s: openArray[byte]): uint64 {.inline.} =
|
||||
if s.len >= 8:
|
||||
let mul = k2 + 2*s.lenU
|
||||
let a = load8(s) + k2
|
||||
let b = load8(s, s.len - 8)
|
||||
let c = rotR(b, 37)*mul + a
|
||||
let d = (rotR(a, 25) + b)*mul
|
||||
len16 c, d, mul
|
||||
elif s.len >= 4:
|
||||
let mul = k2 + 2*s.lenU
|
||||
let a = load4(s).uint64
|
||||
len16 s.lenU + (a shl 3), load4(s, s.len - 4), mul
|
||||
elif s.len > 0:
|
||||
let a = uint32(s[0])
|
||||
let b = uint32(s[s.len shr 1])
|
||||
let c = uint32(s[s.len - 1])
|
||||
let y = a + (b shl 8)
|
||||
let z = s.lenU + (c shl 2)
|
||||
shiftMix(y*k2 xor z*k0)*k2
|
||||
else: k2 # s.len == 0
|
||||
|
||||
proc len17_32(s: openArray[byte]): uint64 {.inline.} =
|
||||
let mul = k2 + 2*s.lenU
|
||||
let a = load8(s)*k1
|
||||
let b = load8(s, 8)
|
||||
let c = load8(s, s.len - 8)*mul
|
||||
let d = load8(s, s.len - 16)*k2
|
||||
len16 rotR(a + b, 43) + rotR(c, 30) + d, a + rotR(b + k2, 18) + c, mul
|
||||
|
||||
proc len33_64(s: openArray[byte]): uint64 {.inline.} =
|
||||
let mul = k2 + 2*s.lenU
|
||||
let a = load8(s)*k2
|
||||
let b = load8(s, 8)
|
||||
let c = load8(s, s.len - 8)*mul
|
||||
let d = load8(s, s.len - 16)*k2
|
||||
let y = rotR(a + b, 43) + rotR(c, 30) + d
|
||||
let z = len16(y, a + rotR(b + k2, 18) + c, mul)
|
||||
let e = load8(s, 16)*mul
|
||||
let f = load8(s, 24)
|
||||
let g = (y + load8(s, s.len - 32))*mul
|
||||
let h = (z + load8(s, s.len - 24))*mul
|
||||
len16 rotR(e + f, 43) + rotR(g, 30) + h, e + rotR(f + a, 18) + g, mul
|
||||
|
||||
type Pair = tuple[first, second: uint64]
|
||||
|
||||
proc weakLen32withSeeds2(w, x, y, z, a, b: uint64): Pair {.inline.} =
|
||||
var a = a + w
|
||||
var b = rotR(b + a + z, 21)
|
||||
let c = a
|
||||
a += x
|
||||
a += y
|
||||
b += rotR(a, 44)
|
||||
result[0] = a + z
|
||||
result[1] = b + c
|
||||
|
||||
proc weakLen32withSeeds(s: openArray[byte]; o: int; a,b: uint64): Pair {.inline.} =
|
||||
weakLen32withSeeds2 load8(s, o ), load8(s, o + 8),
|
||||
load8(s, o + 16), load8(s, o + 24), a, b
|
||||
|
||||
proc hashFarm(s: openArray[byte]): uint64 {.inline.} =
|
||||
if s.len <= 16: return len0_16(s)
|
||||
if s.len <= 32: return len17_32(s)
|
||||
if s.len <= 64: return len33_64(s)
|
||||
const seed = 81u64 # not const to use input `h`
|
||||
var
|
||||
o = 0 # s[] ptr arith -> variable origin variable `o`
|
||||
x = seed
|
||||
y = seed*k1 + 113
|
||||
z = shiftMix(y*k2 + 113)*k2
|
||||
v, w: Pair
|
||||
x = x*k2 + load8(s)
|
||||
let eos = ((s.len - 1) div 64)*64
|
||||
let last64 = eos + ((s.len - 1) and 63) - 63
|
||||
while true:
|
||||
x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1
|
||||
y = rotR(y + v[1] + load8(s, o+48), 42)*k1
|
||||
x = x xor w[1]
|
||||
y += v[0] + load8(s, o+40)
|
||||
z = rotR(z + w[0], 33)*k1
|
||||
v = weakLen32withSeeds(s, o+0 , v[1]*k1, x + w[0])
|
||||
w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16))
|
||||
swap z, x
|
||||
inc o, 64
|
||||
if o == eos: break
|
||||
let mul = k1 + ((z and 0xff) shl 1)
|
||||
o = last64
|
||||
w[0] += (s.lenU - 1) and 63
|
||||
v[0] += w[0]
|
||||
w[0] += v[0]
|
||||
x = rotR(x + y + v[0] + load8(s, o+8), 37)*mul
|
||||
y = rotR(y + v[1] + load8(s, o+48), 42)*mul
|
||||
x = x xor w[1]*9
|
||||
y += v[0]*9 + load8(s, o+40)
|
||||
z = rotR(z + w[0], 33)*mul
|
||||
v = weakLen32withSeeds(s, o+0 , v[1]*mul, x + w[0])
|
||||
w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16))
|
||||
swap z, x
|
||||
len16 len16(v[0],w[0],mul) + shiftMix(y)*k0 + z, len16(v[1],w[1],mul) + x, mul
|
||||
|
||||
template jsNoInt64: untyped =
|
||||
when defined js:
|
||||
when compiles(compileOption("jsbigint64")):
|
||||
when not compileOption("jsbigint64"): true
|
||||
else: false
|
||||
else: false
|
||||
else: false
|
||||
const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false)
|
||||
|
||||
template maybeFailJS_Number =
|
||||
when jsNoInt64() and not defined(nimStringHash2):
|
||||
{.error: "Must use `-d:nimStringHash2` when using `--jsbigint64:off`".}
|
||||
|
||||
proc hash*(x: string): Hash =
|
||||
## Efficient hashing of strings.
|
||||
##
|
||||
@@ -387,11 +539,14 @@ proc hash*(x: string): Hash =
|
||||
## * `hashIgnoreCase <#hashIgnoreCase,string>`_
|
||||
runnableExamples:
|
||||
doAssert hash("abracadabra") != hash("AbracadabrA")
|
||||
|
||||
when nimvm:
|
||||
result = hashVmImpl(x, 0, high(x))
|
||||
maybeFailJS_Number()
|
||||
when not sHash2:
|
||||
result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
|
||||
else:
|
||||
result = murmurHash(toOpenArrayByte(x, 0, high(x)))
|
||||
#when nimvm:
|
||||
# result = hashVmImpl(x, 0, high(x))
|
||||
when true:
|
||||
result = murmurHash(toOpenArrayByte(x, 0, high(x)))
|
||||
|
||||
proc hash*(x: cstring): Hash =
|
||||
## Efficient hashing of null-terminated strings.
|
||||
@@ -400,14 +555,22 @@ proc hash*(x: cstring): Hash =
|
||||
doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA")
|
||||
doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA")
|
||||
|
||||
when nimvm:
|
||||
hashVmImpl(x, 0, high(x))
|
||||
else:
|
||||
when not defined(js):
|
||||
murmurHash(toOpenArrayByte(x, 0, x.high))
|
||||
else:
|
||||
maybeFailJS_Number()
|
||||
when not sHash2:
|
||||
when defined js:
|
||||
let xx = $x
|
||||
murmurHash(toOpenArrayByte(xx, 0, high(xx)))
|
||||
result = cast[Hash](hashFarm(toOpenArrayByte(xx, 0, xx.high)))
|
||||
else:
|
||||
result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
|
||||
else:
|
||||
#when nimvm:
|
||||
# result = hashVmImpl(x, 0, high(x))
|
||||
when true:
|
||||
when not defined(js):
|
||||
result = murmurHash(toOpenArrayByte(x, 0, x.high))
|
||||
else:
|
||||
let xx = $x
|
||||
result = murmurHash(toOpenArrayByte(xx, 0, high(xx)))
|
||||
|
||||
proc hash*(sBuf: string, sPos, ePos: int): Hash =
|
||||
## Efficient hashing of a string buffer, from starting
|
||||
@@ -418,7 +581,11 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash =
|
||||
var a = "abracadabra"
|
||||
doAssert hash(a, 0, 3) == hash(a, 7, 10)
|
||||
|
||||
murmurHash(toOpenArrayByte(sBuf, sPos, ePos))
|
||||
maybeFailJS_Number()
|
||||
when not sHash2:
|
||||
result = cast[Hash](hashFarm(toOpenArrayByte(sBuf, sPos, ePos)))
|
||||
else:
|
||||
murmurHash(toOpenArrayByte(sBuf, sPos, ePos))
|
||||
|
||||
proc hashIgnoreStyle*(x: string): Hash =
|
||||
## Efficient hashing of strings; style is ignored.
|
||||
@@ -553,12 +720,18 @@ proc hash*[A](x: openArray[A]): Hash =
|
||||
## Efficient hashing of arrays and sequences.
|
||||
## There must be a `hash` proc defined for the element type `A`.
|
||||
when A is byte:
|
||||
result = murmurHash(x)
|
||||
elif A is char:
|
||||
when nimvm:
|
||||
result = hashVmImplChar(x, 0, x.high)
|
||||
when not sHash2:
|
||||
result = cast[Hash](hashFarm(x))
|
||||
else:
|
||||
result = murmurHash(toOpenArrayByte(x, 0, x.high))
|
||||
result = murmurHash(x)
|
||||
elif A is char:
|
||||
when not sHash2:
|
||||
result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
|
||||
else:
|
||||
#when nimvm:
|
||||
# result = hashVmImplChar(x, 0, x.high)
|
||||
when true:
|
||||
result = murmurHash(toOpenArrayByte(x, 0, x.high))
|
||||
else:
|
||||
result = 0
|
||||
for a in x:
|
||||
@@ -574,17 +747,24 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
|
||||
runnableExamples:
|
||||
let a = [1, 2, 5, 1, 2, 6]
|
||||
doAssert hash(a, 0, 1) == hash(a, 3, 4)
|
||||
|
||||
when A is byte:
|
||||
when nimvm:
|
||||
result = hashVmImplByte(aBuf, sPos, ePos)
|
||||
maybeFailJS_Number()
|
||||
when not sHash2:
|
||||
result = cast[Hash](hashFarm(toOpenArray(aBuf, sPos, ePos)))
|
||||
else:
|
||||
result = murmurHash(toOpenArray(aBuf, sPos, ePos))
|
||||
#when nimvm:
|
||||
# result = hashVmImplByte(aBuf, sPos, ePos)
|
||||
when true:
|
||||
result = murmurHash(toOpenArray(aBuf, sPos, ePos))
|
||||
elif A is char:
|
||||
when nimvm:
|
||||
result = hashVmImplChar(aBuf, sPos, ePos)
|
||||
maybeFailJS_Number()
|
||||
when not sHash2:
|
||||
result = cast[Hash](hashFarm(toOpenArrayByte(aBuf, sPos, ePos)))
|
||||
else:
|
||||
result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos))
|
||||
#when nimvm:
|
||||
# result = hashVmImplChar(aBuf, sPos, ePos)
|
||||
when true:
|
||||
result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos))
|
||||
else:
|
||||
for i in sPos .. ePos:
|
||||
result = result !& hash(aBuf[i])
|
||||
|
||||
@@ -164,7 +164,8 @@ func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues =
|
||||
## To access multiple values of a key, use the overloaded `[]` below or
|
||||
## to get all of them access the `table` field directly.
|
||||
{.cast(noSideEffect).}:
|
||||
return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues
|
||||
let tmp = headers.table[headers.toCaseInsensitive(key)]
|
||||
return HttpHeaderValues(tmp)
|
||||
|
||||
converter toString*(values: HttpHeaderValues): string =
|
||||
return seq[string](values)[0]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1007,7 +1007,7 @@ when defined(js):
|
||||
{.emit: """for (var property in `x`) {
|
||||
if (`x`.hasOwnProperty(property)) {
|
||||
""".}
|
||||
|
||||
|
||||
var nimProperty: cstring
|
||||
var nimValue: JsObject
|
||||
{.emit: "`nimProperty` = property; `nimValue` = `x`[property];".}
|
||||
|
||||
@@ -104,9 +104,9 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int =
|
||||
result = 0
|
||||
|
||||
proc handleCR*(L: var BaseLexer, pos: int): int =
|
||||
## Call this if you scanned over '\c' in the buffer; it returns the
|
||||
## Call this if you scanned over `'\c'` in the buffer; it returns the
|
||||
## position to continue the scanning from. `pos` must be the position
|
||||
## of the '\c'.
|
||||
## of the `'\c'`.
|
||||
assert(L.buf[pos] == '\c')
|
||||
inc(L.lineNumber)
|
||||
result = fillBaseLexer(L, pos)
|
||||
@@ -115,9 +115,9 @@ proc handleCR*(L: var BaseLexer, pos: int): int =
|
||||
L.lineStart = result
|
||||
|
||||
proc handleLF*(L: var BaseLexer, pos: int): int =
|
||||
## Call this if you scanned over '\L' in the buffer; it returns the
|
||||
## Call this if you scanned over `'\L'` in the buffer; it returns the
|
||||
## position to continue the scanning from. `pos` must be the position
|
||||
## of the '\L'.
|
||||
## of the `'\L'`.
|
||||
assert(L.buf[pos] == '\L')
|
||||
inc(L.lineNumber)
|
||||
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
|
||||
|
||||
@@ -58,6 +58,7 @@ import std/private/since
|
||||
# of the standard library!
|
||||
|
||||
import std/[bitops, fenv]
|
||||
import system/countbits_impl
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -155,7 +156,7 @@ func fac*(n: int): int =
|
||||
|
||||
{.push checks: off, line_dir: off, stack_trace: off.}
|
||||
|
||||
when defined(posix) and not defined(genode):
|
||||
when defined(posix) and not defined(genode) and not defined(macosx):
|
||||
{.passl: "-lm".}
|
||||
|
||||
const
|
||||
@@ -224,7 +225,7 @@ when defined(js):
|
||||
return (num & ~(1 << bitPos)) | (bitVal << bitPos);
|
||||
}
|
||||
`b`[1] = updateBit(`b`[1], 31, `sgn`);
|
||||
`result` = `a`[0]
|
||||
`result` = `a`[0];
|
||||
""".}
|
||||
|
||||
proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} =
|
||||
@@ -1229,40 +1230,42 @@ func gcd*[T](x, y: T): T =
|
||||
swap x, y
|
||||
abs x
|
||||
|
||||
func gcd*(x, y: SomeInteger): SomeInteger =
|
||||
## Computes the greatest common (positive) divisor of `x` and `y`,
|
||||
## using the binary GCD (aka Stein's) algorithm.
|
||||
##
|
||||
## **See also:**
|
||||
## * `gcd func <#gcd,T,T>`_ for a float version
|
||||
## * `lcm func <#lcm,T,T>`_
|
||||
runnableExamples:
|
||||
doAssert gcd(12, 8) == 4
|
||||
doAssert gcd(17, 63) == 1
|
||||
|
||||
when x is SomeSignedInt:
|
||||
var x = abs(x)
|
||||
else:
|
||||
var x = x
|
||||
when y is SomeSignedInt:
|
||||
var y = abs(y)
|
||||
else:
|
||||
var y = y
|
||||
|
||||
if x == 0:
|
||||
return y
|
||||
if y == 0:
|
||||
return x
|
||||
|
||||
let shift = countTrailingZeroBits(x or y)
|
||||
y = y shr countTrailingZeroBits(y)
|
||||
while x != 0:
|
||||
x = x shr countTrailingZeroBits(x)
|
||||
if y > x:
|
||||
swap y, x
|
||||
x -= y
|
||||
y shl shift
|
||||
|
||||
when useBuiltins:
|
||||
## this func uses bitwise comparisons from C compilers, which are not always available.
|
||||
func gcd*(x, y: SomeInteger): SomeInteger =
|
||||
## Computes the greatest common (positive) divisor of `x` and `y`,
|
||||
## using the binary GCD (aka Stein's) algorithm.
|
||||
##
|
||||
## **See also:**
|
||||
## * `gcd func <#gcd,T,T>`_ for a float version
|
||||
## * `lcm func <#lcm,T,T>`_
|
||||
runnableExamples:
|
||||
doAssert gcd(12, 8) == 4
|
||||
doAssert gcd(17, 63) == 1
|
||||
|
||||
when x is SomeSignedInt:
|
||||
var x = abs(x)
|
||||
else:
|
||||
var x = x
|
||||
when y is SomeSignedInt:
|
||||
var y = abs(y)
|
||||
else:
|
||||
var y = y
|
||||
|
||||
if x == 0:
|
||||
return y
|
||||
if y == 0:
|
||||
return x
|
||||
|
||||
let shift = countTrailingZeroBits(x or y)
|
||||
y = y shr countTrailingZeroBits(y)
|
||||
while x != 0:
|
||||
x = x shr countTrailingZeroBits(x)
|
||||
if y > x:
|
||||
swap y, x
|
||||
x -= y
|
||||
y shl shift
|
||||
|
||||
func gcd*[T](x: openArray[T]): T {.since: (1, 1).} =
|
||||
## Computes the greatest common (positive) divisor of the elements of `x`.
|
||||
##
|
||||
|
||||
@@ -35,12 +35,12 @@ proc newEIO(msg: string): ref IOError =
|
||||
new(result)
|
||||
result.msg = msg
|
||||
|
||||
proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode =
|
||||
## Set the size of open file pointed to by `fh` to `newFileSize` if != -1.
|
||||
## Space is only allocated if that is cheaper than writing to the file. This
|
||||
## routine returns the last OSErrorCode found rather than raising to support
|
||||
## old rollback/clean-up code style. [ Should maybe move to std/osfiles. ]
|
||||
if newFileSize == -1:
|
||||
proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
|
||||
## Set the size of open file pointed to by `fh` to `newFileSize` if != -1,
|
||||
## allocating | freeing space from the file system. This routine returns the
|
||||
## last OSErrorCode found rather than raising to support old rollback/clean-up
|
||||
## code style. [ Should maybe move to std/osfiles. ]
|
||||
if newFileSize < 0 or newFileSize == oldSize:
|
||||
return
|
||||
when defined(windows):
|
||||
var sizeHigh = int32(newFileSize shr 32)
|
||||
@@ -51,14 +51,18 @@ proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode =
|
||||
setEndOfFile(fh) == 0:
|
||||
result = lastErr
|
||||
else:
|
||||
var e: cint # posix_fallocate truncates up when needed.
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError() # fallback arguable; Most portable, but allows SEGV
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
if newFileSize > oldSize: # grow the file
|
||||
var e: cint # posix_fallocate truncates up when needed.
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError() # fallback arguable; Most portable BUT allows SEGV
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
else: # shrink the file
|
||||
if ftruncate(fh.cint, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
|
||||
type
|
||||
MemFile* = object ## represents a memory mapped file
|
||||
@@ -255,41 +259,31 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
flags = flags or O_CREAT or O_TRUNC
|
||||
var permissionsMode = S_IRUSR or S_IWUSR
|
||||
result.handle = open(filename, flags, permissionsMode)
|
||||
if result.handle != -1:
|
||||
if (let e = setFileSize(result.handle.FileHandle, newFileSize);
|
||||
e != 0.OSErrorCode): fail(e, "error setting file size")
|
||||
else:
|
||||
result.handle = open(filename, flags)
|
||||
|
||||
if result.handle == -1:
|
||||
# XXX: errno is supposed to be set here
|
||||
# Is there an exception that wraps it?
|
||||
fail(osLastError(), "error opening file")
|
||||
|
||||
if (let e = setFileSize(result.handle.FileHandle, newFileSize);
|
||||
e != 0.OSErrorCode): fail(e, "error setting file size")
|
||||
|
||||
if mappedSize != -1:
|
||||
result.size = mappedSize
|
||||
else:
|
||||
var stat: Stat
|
||||
if mappedSize != -1: #XXX Logic here differs from `when windows` branch ..
|
||||
result.size = mappedSize #.. which always fstats&Uses min(mappedSize, st).
|
||||
else: # if newFileSize!=-1: result.size=newFileSize # if trust setFileSize
|
||||
var stat: Stat #^^.. BUT some FSes (eg. Linux HugeTLBfs) round to 2MiB.
|
||||
if fstat(result.handle, stat) != -1:
|
||||
# XXX: Hmm, this could be unsafe
|
||||
# Why is mmap taking int anyway?
|
||||
result.size = int(stat.st_size)
|
||||
result.size = stat.st_size.int # int may be 32-bit-unsafe for 2..<4 GiB
|
||||
else:
|
||||
fail(osLastError(), "error getting file size")
|
||||
|
||||
result.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags
|
||||
#Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
|
||||
# Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
|
||||
if int(result.flags and MAP_PRIVATE) == 0:
|
||||
result.flags = result.flags or MAP_SHARED
|
||||
|
||||
result.mem = mmap(
|
||||
nil,
|
||||
result.size,
|
||||
if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
|
||||
result.flags,
|
||||
result.handle,
|
||||
offset)
|
||||
|
||||
let pr = if readonly: PROT_READ else: PROT_READ or PROT_WRITE
|
||||
result.mem = mmap(nil, result.size, pr, result.flags, result.handle, offset)
|
||||
if result.mem == cast[pointer](MAP_FAILED):
|
||||
fail(osLastError(), "file mapping failed")
|
||||
|
||||
@@ -353,7 +347,7 @@ proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if newFileSize != f.size:
|
||||
if (let e = setFileSize(f.handle.FileHandle, newFileSize);
|
||||
if (let e = setFileSize(f.handle.FileHandle, newFileSize, f.size);
|
||||
e != 0.OSErrorCode): raiseOSError(e)
|
||||
when defined(linux): #Maybe NetBSD, too?
|
||||
# On Linux this can be over 100 times faster than a munmap,mmap cycle.
|
||||
|
||||
@@ -432,6 +432,10 @@ const mimes* = {
|
||||
"msty": "application/vnd.muvee.style",
|
||||
"taglet": "application/vnd.mynfc",
|
||||
"nlu": "application/vnd.neurolanguage.nlu",
|
||||
"nim": "text/nim",
|
||||
"nimble": "text/nimble",
|
||||
"nimf": "text/nim",
|
||||
"nims": "text/nim",
|
||||
"ntf": "application/vnd.nitf",
|
||||
"nitf": "application/vnd.nitf",
|
||||
"nnd": "application/vnd.noblenet-directory",
|
||||
|
||||
@@ -2040,8 +2040,10 @@ proc dial*(address: string, port: Port,
|
||||
if success:
|
||||
result = newSocket(lastFd, domain, sockType, protocol, buffered)
|
||||
elif lastError != 0.OSErrorCode:
|
||||
lastFd.close()
|
||||
raiseOSError(lastError)
|
||||
else:
|
||||
lastFd.close()
|
||||
raise newException(IOError, "Couldn't resolve address: " & address)
|
||||
|
||||
proc connect*(socket: Socket, address: string,
|
||||
|
||||
@@ -117,9 +117,10 @@ proc option*[T](val: sink T): Option[T] {.inline.} =
|
||||
assert option[Foo](nil).isNone
|
||||
assert option(42).isSome
|
||||
|
||||
result.val = val
|
||||
when T isnot SomePointer:
|
||||
result.has = true
|
||||
when T is SomePointer:
|
||||
result = Option[T](val: val)
|
||||
else:
|
||||
result = Option[T](has: true, val: val)
|
||||
|
||||
proc some*[T](val: sink T): Option[T] {.inline.} =
|
||||
## Returns an `Option` that has the value `val`.
|
||||
@@ -136,10 +137,9 @@ proc some*[T](val: sink T): Option[T] {.inline.} =
|
||||
|
||||
when T is SomePointer:
|
||||
assert not val.isNil
|
||||
result.val = val
|
||||
result = Option[T](val: val)
|
||||
else:
|
||||
result.has = true
|
||||
result.val = val
|
||||
result = Option[T](has: true, val: val)
|
||||
|
||||
proc none*(T: typedesc): Option[T] {.inline.} =
|
||||
## Returns an `Option` for this type that has no value.
|
||||
|
||||
@@ -692,7 +692,10 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT
|
||||
|
||||
proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} =
|
||||
## Sleeps `milsecs` milliseconds.
|
||||
## A negative `milsecs` causes sleep to return immediately.
|
||||
when defined(windows):
|
||||
if milsecs < 0:
|
||||
return # fixes #23732
|
||||
winlean.sleep(int32(milsecs))
|
||||
else:
|
||||
var a, b: Timespec
|
||||
@@ -754,14 +757,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
|
||||
## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows,
|
||||
## or a 'Stat' structure on posix
|
||||
when defined(windows):
|
||||
template merge(a, b): untyped =
|
||||
int64(
|
||||
template merge[T](a, b): untyped =
|
||||
cast[T](
|
||||
(uint64(cast[uint32](a))) or
|
||||
(uint64(cast[uint32](b)) shl 32)
|
||||
)
|
||||
formalInfo.id.device = rawInfo.dwVolumeSerialNumber
|
||||
formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
|
||||
formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
|
||||
formalInfo.id.file = merge[FileId](rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
|
||||
formalInfo.size = merge[BiggestInt](rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
|
||||
formalInfo.linkCount = rawInfo.nNumberOfLinks
|
||||
formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime))
|
||||
formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime))
|
||||
|
||||
@@ -212,7 +212,7 @@ proc processID*(p: Process): int {.rtl, extern: "nosp$1".} =
|
||||
return p.id
|
||||
|
||||
proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
|
||||
extern: "nosp$1", raises: [OSError, ValueError], tags: [].}
|
||||
extern: "nosp$1", raises: [OSError, ValueError], tags: [TimeEffect].}
|
||||
## Waits for the process to finish and returns `p`'s error code.
|
||||
##
|
||||
## .. warning:: Be careful when using `waitForExit` for processes created without
|
||||
@@ -457,7 +457,7 @@ proc execProcesses*(cmds: openArray[string],
|
||||
if afterRunEvent != nil: afterRunEvent(i, p)
|
||||
close(p)
|
||||
|
||||
iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} =
|
||||
iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} =
|
||||
## Convenience iterator for working with `startProcess` to read data from a
|
||||
## background process.
|
||||
##
|
||||
@@ -487,7 +487,7 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raise
|
||||
discard waitForExit(p)
|
||||
|
||||
proc readLines*(p: Process): (seq[string], int) {.since: (1, 3),
|
||||
raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} =
|
||||
raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} =
|
||||
## Convenience function for working with `startProcess` to read data from a
|
||||
## background process.
|
||||
##
|
||||
@@ -1353,114 +1353,63 @@ elif not defined(useNimRtl):
|
||||
result = exitStatusLikeShell(p.exitStatus)
|
||||
|
||||
else:
|
||||
import std/times
|
||||
|
||||
const
|
||||
hasThreadSupport = compileOption("threads") and not defined(nimscript)
|
||||
import std/times except getTime
|
||||
import std/monotimes
|
||||
|
||||
proc waitForExit(p: Process, timeout: int = -1): int =
|
||||
template adjustTimeout(t, s, e: Timespec) =
|
||||
var diff: int
|
||||
var b: Timespec
|
||||
b.tv_sec = e.tv_sec
|
||||
b.tv_nsec = e.tv_nsec
|
||||
e.tv_sec = e.tv_sec - s.tv_sec
|
||||
if e.tv_nsec >= s.tv_nsec:
|
||||
e.tv_nsec -= s.tv_nsec
|
||||
else:
|
||||
if e.tv_sec == posix.Time(0):
|
||||
raise newException(ValueError, "System time was modified")
|
||||
else:
|
||||
diff = s.tv_nsec - e.tv_nsec
|
||||
e.tv_nsec = 1_000_000_000 - diff
|
||||
t.tv_sec = t.tv_sec - e.tv_sec
|
||||
if t.tv_nsec >= e.tv_nsec:
|
||||
t.tv_nsec -= e.tv_nsec
|
||||
else:
|
||||
t.tv_sec = t.tv_sec - posix.Time(1)
|
||||
diff = e.tv_nsec - t.tv_nsec
|
||||
t.tv_nsec = 1_000_000_000 - diff
|
||||
s.tv_sec = b.tv_sec
|
||||
s.tv_nsec = b.tv_nsec
|
||||
|
||||
if p.exitFlag:
|
||||
return exitStatusLikeShell(p.exitStatus)
|
||||
|
||||
if timeout == -1:
|
||||
var status: cint = 1
|
||||
if timeout < 0:
|
||||
# Backwards compatibility with previous verison to
|
||||
# handle cases where timeout == -1, but extend
|
||||
# to handle cases where timeout < 0
|
||||
var status: cint
|
||||
if waitpid(p.id, status, 0) < 0:
|
||||
raiseOSError(osLastError())
|
||||
p.exitFlag = true
|
||||
p.exitStatus = status
|
||||
else:
|
||||
var nmask, omask: Sigset
|
||||
var sinfo: SigInfo
|
||||
var stspec, enspec, tmspec: Timespec
|
||||
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, SIGCHLD)
|
||||
|
||||
when hasThreadSupport:
|
||||
if pthread_sigmask(SIG_BLOCK, nmask, omask) == -1:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if sigprocmask(SIG_BLOCK, nmask, omask) == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if timeout >= 1000:
|
||||
tmspec.tv_sec = posix.Time(timeout div 1_000)
|
||||
tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
|
||||
else:
|
||||
tmspec.tv_sec = posix.Time(0)
|
||||
tmspec.tv_nsec = (timeout * 1_000_000)
|
||||
|
||||
try:
|
||||
if clock_gettime(CLOCK_REALTIME, stspec) == -1:
|
||||
raiseOSError(osLastError())
|
||||
while true:
|
||||
let res = sigtimedwait(nmask, sinfo, tmspec)
|
||||
if res == SIGCHLD:
|
||||
if sinfo.si_pid == p.id:
|
||||
var status: cint = 1
|
||||
if waitpid(p.id, status, 0) < 0:
|
||||
raiseOSError(osLastError())
|
||||
p.exitFlag = true
|
||||
p.exitStatus = status
|
||||
break
|
||||
else:
|
||||
# we have SIGCHLD, but not for process we are waiting,
|
||||
# so we need to adjust timeout value and continue
|
||||
if clock_gettime(CLOCK_REALTIME, enspec) == -1:
|
||||
raiseOSError(osLastError())
|
||||
adjustTimeout(tmspec, stspec, enspec)
|
||||
elif res < 0:
|
||||
let err = osLastError()
|
||||
if err.cint == EINTR:
|
||||
# we have received another signal, so we need to
|
||||
# adjust timeout and continue
|
||||
if clock_gettime(CLOCK_REALTIME, enspec) == -1:
|
||||
raiseOSError(osLastError())
|
||||
adjustTimeout(tmspec, stspec, enspec)
|
||||
elif err.cint == EAGAIN:
|
||||
# timeout expired, so we trying to kill process
|
||||
if posix.kill(p.id, SIGKILL) == -1:
|
||||
raiseOSError(osLastError())
|
||||
var status: cint = 1
|
||||
if waitpid(p.id, status, 0) < 0:
|
||||
raiseOSError(osLastError())
|
||||
p.exitFlag = true
|
||||
p.exitStatus = status
|
||||
break
|
||||
else:
|
||||
raiseOSError(err)
|
||||
finally:
|
||||
when hasThreadSupport:
|
||||
if pthread_sigmask(SIG_UNBLOCK, nmask, omask) == -1:
|
||||
raiseOSError(osLastError())
|
||||
# Max 50ms delay
|
||||
const maxWait = initDuration(milliseconds = 50)
|
||||
let wait = initDuration(milliseconds = timeout)
|
||||
let deadline = getMonoTime() + wait
|
||||
# starting 50μs delay
|
||||
var delay = initDuration(microseconds = 50)
|
||||
|
||||
while true:
|
||||
var status: cint
|
||||
let pid = waitpid(p.id, status, WNOHANG)
|
||||
if p.id == pid :
|
||||
p.exitFlag = true
|
||||
p.exitStatus = status
|
||||
break
|
||||
elif pid.int == -1:
|
||||
raiseOsError(osLastError())
|
||||
else:
|
||||
if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
|
||||
raiseOSError(osLastError())
|
||||
# Continue waiting if needed
|
||||
if getMonoTime() >= deadline:
|
||||
# Previous version of `waitForExit`
|
||||
# foricibly killed the process.
|
||||
# We keep this so we don't break programs
|
||||
# that depend on this behavior
|
||||
if posix.kill(p.id, SIGKILL) < 0:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
const max = 1_000_000_000
|
||||
let
|
||||
newWait = getMonoTime() + delay
|
||||
ticks = newWait.ticks()
|
||||
ns = ticks mod max
|
||||
secs = ticks div max
|
||||
var
|
||||
waitSpec: TimeSpec
|
||||
unused: Timespec
|
||||
waitSpec.tv_sec = posix.Time(secs)
|
||||
waitSpec.tv_nsec = clong ns
|
||||
discard posix.clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, waitSpec, unused)
|
||||
let remaining = deadline - getMonoTime()
|
||||
delay = min([delay * 2, remaining, maxWait])
|
||||
|
||||
result = exitStatusLikeShell(p.exitStatus)
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import std/strutils
|
||||
import std/os
|
||||
|
||||
type
|
||||
@@ -249,9 +250,20 @@ proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
|
||||
result.cmds[i] = cmdline[i]
|
||||
else:
|
||||
when declared(paramCount):
|
||||
result.cmds = newSeq[string](paramCount())
|
||||
for i in countup(1, paramCount()):
|
||||
result.cmds[i-1] = paramStr(i)
|
||||
when defined(nimscript):
|
||||
var ctr = 0
|
||||
var firstNimsFound = false
|
||||
for i in countup(0, paramCount()):
|
||||
if firstNimsFound:
|
||||
result.cmds[ctr] = paramStr(i)
|
||||
inc ctr, 1
|
||||
if paramStr(i).endsWith(".nims") and not firstNimsFound:
|
||||
firstNimsFound = true
|
||||
result.cmds = newSeq[string](paramCount()-i)
|
||||
else:
|
||||
result.cmds = newSeq[string](paramCount())
|
||||
for i in countup(1, paramCount()):
|
||||
result.cmds[i-1] = paramStr(i)
|
||||
else:
|
||||
# we cannot provide this for NimRtl creation on Posix, because we can't
|
||||
# access the command line arguments then!
|
||||
|
||||
@@ -460,6 +460,8 @@ proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {.
|
||||
var res: BiggestInt
|
||||
doAssert parseBiggestInt("9223372036854775807", res) == 19
|
||||
doAssert res == 9223372036854775807
|
||||
doAssert parseBiggestInt("-2024_05_09", res) == 11
|
||||
doAssert res == -20240509
|
||||
var res = BiggestInt(0)
|
||||
# use 'res' for exception safety (don't write to 'number' in case of an
|
||||
# overflow exception):
|
||||
@@ -474,10 +476,8 @@ proc parseInt*(s: openArray[char], number: var int): int {.
|
||||
## `ValueError` is raised if the parsed integer is out of the valid range.
|
||||
runnableExamples:
|
||||
var res: int
|
||||
doAssert parseInt("2019", res, 0) == 4
|
||||
doAssert res == 2019
|
||||
doAssert parseInt("2019", res, 2) == 2
|
||||
doAssert res == 19
|
||||
doAssert parseInt("-2024_05_02", res) == 11
|
||||
doAssert res == -20240502
|
||||
var res = BiggestInt(0)
|
||||
result = parseBiggestInt(s, res)
|
||||
when sizeof(int) <= 4:
|
||||
@@ -992,6 +992,10 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSid
|
||||
var res: BiggestInt
|
||||
doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
|
||||
doAssert res == 9223372036854775807
|
||||
doAssert parseBiggestInt("-2024_05_09", res) == 11
|
||||
doAssert res == -20240509
|
||||
doAssert parseBiggestInt("-2024_05_02", res, 7) == 4
|
||||
doAssert res == 502
|
||||
parseBiggestInt(s.toOpenArray(start, s.high), number)
|
||||
|
||||
proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raises: [ValueError].} =
|
||||
@@ -1000,10 +1004,10 @@ proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raise
|
||||
## `ValueError` is raised if the parsed integer is out of the valid range.
|
||||
runnableExamples:
|
||||
var res: int
|
||||
doAssert parseInt("2019", res, 0) == 4
|
||||
doAssert res == 2019
|
||||
doAssert parseInt("2019", res, 2) == 2
|
||||
doAssert res == 19
|
||||
doAssert parseInt("-2024_05_02", res) == 11
|
||||
doAssert res == -20240502
|
||||
doAssert parseInt("-2024_05_02", res, 7) == 4
|
||||
doAssert res == 502
|
||||
parseInt(s.toOpenArray(start, s.high), number)
|
||||
|
||||
|
||||
|
||||
@@ -27,20 +27,17 @@ func expandTabs*(s: string, tabSize: int = 8): string =
|
||||
doAssert expandTabs("a\tb\n\txy\t", 3) == "a b\n xy "
|
||||
|
||||
result = newStringOfCap(s.len + s.len shr 2)
|
||||
var pos = 0
|
||||
|
||||
template addSpaces(n) =
|
||||
for j in 0 ..< n:
|
||||
for _ in 1..n:
|
||||
result.add(' ')
|
||||
pos += 1
|
||||
pos += n
|
||||
|
||||
for i in 0 ..< len(s):
|
||||
let c = s[i]
|
||||
var pos = 0
|
||||
let denominator = if tabSize > 0: tabSize else: 1
|
||||
for c in s:
|
||||
if c == '\t':
|
||||
let
|
||||
denominator = if tabSize > 0: tabSize else: 1
|
||||
numSpaces = tabSize - pos mod denominator
|
||||
|
||||
let numSpaces = tabSize - pos mod denominator
|
||||
addSpaces(numSpaces)
|
||||
else:
|
||||
result.add(c)
|
||||
|
||||
@@ -334,9 +334,9 @@ func normalize*(s: string): string {.rtl, extern: "nsuNormalize".} =
|
||||
func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} =
|
||||
## Compares two strings in a case insensitive manner. Returns:
|
||||
##
|
||||
## | 0 if a == b
|
||||
## | < 0 if a < b
|
||||
## | > 0 if a > b
|
||||
## | `0` if a == b
|
||||
## | `< 0` if a < b
|
||||
## | `> 0` if a > b
|
||||
runnableExamples:
|
||||
doAssert cmpIgnoreCase("FooBar", "foobar") == 0
|
||||
doAssert cmpIgnoreCase("bar", "Foo") < 0
|
||||
@@ -354,9 +354,9 @@ func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} =
|
||||
##
|
||||
## Returns:
|
||||
##
|
||||
## | 0 if a == b
|
||||
## | < 0 if a < b
|
||||
## | > 0 if a > b
|
||||
## | `0` if a == b
|
||||
## | `< 0` if a < b
|
||||
## | `> 0` if a > b
|
||||
runnableExamples:
|
||||
doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0
|
||||
doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0
|
||||
@@ -565,7 +565,7 @@ iterator rsplit*(s: string, sep: char,
|
||||
maxsplit: int = -1): string =
|
||||
## Splits the string `s` into substrings from the right using a
|
||||
## string separator. Works exactly the same as `split iterator
|
||||
## <#split.i,string,char,int>`_ except in reverse order.
|
||||
## <#split.i,string,char,int>`_ except in **reverse** order.
|
||||
##
|
||||
## ```nim
|
||||
## for piece in "foo:bar".rsplit(':'):
|
||||
@@ -592,7 +592,7 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
|
||||
maxsplit: int = -1): string =
|
||||
## Splits the string `s` into substrings from the right using a
|
||||
## string separator. Works exactly the same as `split iterator
|
||||
## <#split.i,string,char,int>`_ except in reverse order.
|
||||
## <#split.i,string,char,int>`_ except in **reverse** order.
|
||||
##
|
||||
## ```nim
|
||||
## for piece in "foo bar".rsplit(WhiteSpace):
|
||||
@@ -622,7 +622,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
|
||||
keepSeparators: bool = false): string =
|
||||
## Splits the string `s` into substrings from the right using a
|
||||
## string separator. Works exactly the same as `split iterator
|
||||
## <#split.i,string,string,int>`_ except in reverse order.
|
||||
## <#split.i,string,string,int>`_ except in **reverse** order.
|
||||
##
|
||||
## ```nim
|
||||
## for piece in "foothebar".rsplit("the"):
|
||||
@@ -805,7 +805,7 @@ func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
|
||||
func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl,
|
||||
extern: "nsuRSplitChar".} =
|
||||
## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a func
|
||||
## that returns a sequence of substrings.
|
||||
## that returns a sequence of substrings in original order.
|
||||
##
|
||||
## A possible common use case for `rsplit` is path manipulation,
|
||||
## particularly on systems that don't use a common delimiter.
|
||||
@@ -835,7 +835,7 @@ func rsplit*(s: string, seps: set[char] = Whitespace,
|
||||
maxsplit: int = -1): seq[string]
|
||||
{.rtl, extern: "nsuRSplitCharSet".} =
|
||||
## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
|
||||
## func that returns a sequence of substrings.
|
||||
## func that returns a sequence of substrings in original order.
|
||||
##
|
||||
## A possible common use case for `rsplit` is path manipulation,
|
||||
## particularly on systems that don't use a common delimiter.
|
||||
@@ -867,7 +867,7 @@ func rsplit*(s: string, seps: set[char] = Whitespace,
|
||||
func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
|
||||
extern: "nsuRSplitString".} =
|
||||
## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a func
|
||||
## that returns a sequence of substrings.
|
||||
## that returns a sequence of substrings in original order.
|
||||
##
|
||||
## A possible common use case for `rsplit` is path manipulation,
|
||||
## particularly on systems that don't use a common delimiter.
|
||||
@@ -1307,7 +1307,7 @@ func parseEnum*[T: enum](s: string): T =
|
||||
## type contains multiple fields with the same string value.
|
||||
##
|
||||
## Raises `ValueError` for an invalid value in `s`. The comparison is
|
||||
## done in a style insensitive way.
|
||||
## done in a style insensitive way (first letter is still case-sensitive).
|
||||
runnableExamples:
|
||||
type
|
||||
MyEnum = enum
|
||||
@@ -1327,7 +1327,7 @@ func parseEnum*[T: enum](s: string, default: T): T =
|
||||
## type contains multiple fields with the same string value.
|
||||
##
|
||||
## Uses `default` for an invalid value in `s`. The comparison is done in a
|
||||
## style insensitive way.
|
||||
## style insensitive way (first letter is still case-sensitive).
|
||||
runnableExamples:
|
||||
type
|
||||
MyEnum = enum
|
||||
|
||||
@@ -345,7 +345,7 @@ proc collectImpl(init, body: NimNode): NimNode {.since: (1, 1).} =
|
||||
let res = genSym(nskVar, "collectResult")
|
||||
var bracketExpr: NimNode
|
||||
if init != nil:
|
||||
expectKind init, {nnkCall, nnkIdent, nnkSym, nnkClosedSymChoice, nnkOpenSymChoice}
|
||||
expectKind init, {nnkCall, nnkIdent, nnkSym, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}
|
||||
bracketExpr = newTree(nnkBracketExpr,
|
||||
if init.kind in {nnkCall, nnkClosedSymChoice, nnkOpenSymChoice}:
|
||||
freshIdentNodes(init[0]) else: freshIdentNodes(init))
|
||||
|
||||
@@ -131,9 +131,10 @@
|
||||
=========== ================================================================================= ==============================================
|
||||
|
||||
Other strings can be inserted by putting them in `''`. For example
|
||||
`hh'->'mm` will give `01->56`. The following characters can be
|
||||
inserted without quoting them: `:` `-` `(` `)` `/` `[` `]`
|
||||
`,`. A literal `'` can be specified with `''`.
|
||||
`hh'->'mm` will give `01->56`. In addition to spaces,
|
||||
the following characters can be inserted without quoting them:
|
||||
`:` `-` `,` `.` `(` `)` `/` `[` `]`.
|
||||
A literal `'` can be specified with `''`.
|
||||
|
||||
However you don't need to necessarily separate format patterns, as an
|
||||
unambiguous format string like `yyyyMMddhhmmss` is also valid (although
|
||||
@@ -1498,11 +1499,11 @@ proc `-=`*(a: var DateTime, b: Duration) =
|
||||
a = a - b
|
||||
|
||||
proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
|
||||
## Gets the current local date as a string of the format `YYYY-MM-DD`.
|
||||
## Gets the current local date as a string of the format `YYYY-MM-dd`.
|
||||
runnableExamples:
|
||||
echo getDateStr(now() - 1.months)
|
||||
assertDateTimeInitialized dt
|
||||
result = newStringOfCap(10) # len("YYYY-MM-DD") == 10
|
||||
result = newStringOfCap(10) # len("YYYY-MM-dd") == 10
|
||||
result.addInt dt.year
|
||||
result.add '-'
|
||||
result.add intToStr(dt.monthZero, 2)
|
||||
@@ -1631,7 +1632,7 @@ const
|
||||
"Sunday"],
|
||||
)
|
||||
|
||||
FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ','}
|
||||
FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ',', '.'}
|
||||
|
||||
proc `$`*(f: TimeFormat): string =
|
||||
## Returns the format string that was used to construct `f`.
|
||||
|
||||
@@ -836,9 +836,9 @@ proc toRunes*(s: openArray[char]): seq[Rune] =
|
||||
proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} =
|
||||
## Compares two UTF-8 strings and ignores the case. Returns:
|
||||
##
|
||||
## | 0 if a == b
|
||||
## | < 0 if a < b
|
||||
## | > 0 if a > b
|
||||
## | `0` if a == b
|
||||
## | `< 0` if a < b
|
||||
## | `> 0` if a > b
|
||||
var i = 0
|
||||
var j = 0
|
||||
var ar, br: Rune
|
||||
@@ -1375,9 +1375,9 @@ proc toRunes*(s: string): seq[Rune] {.inline.} =
|
||||
proc cmpRunesIgnoreCase*(a, b: string): int {.inline.} =
|
||||
## Compares two UTF-8 strings and ignores the case. Returns:
|
||||
##
|
||||
## | 0 if a == b
|
||||
## | < 0 if a < b
|
||||
## | > 0 if a > b
|
||||
## | `0` if a == b
|
||||
## | `< 0` if a < b
|
||||
## | `> 0` if a > b
|
||||
cmpRunesIgnoreCase(a.toOa(), b.toOa())
|
||||
|
||||
proc reversed*(s: string): string {.inline.} =
|
||||
|
||||
@@ -109,11 +109,11 @@ when defined(js):
|
||||
return n.toString().match(/^-?\d+$/);
|
||||
}
|
||||
if (Number.isSafeInteger(`a`))
|
||||
`result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
|
||||
`result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0";
|
||||
else {
|
||||
`result` = `a`+""
|
||||
`result` = `a`+"";
|
||||
if(nimOnlyDigitsOrMinus(`result`)){
|
||||
`result` = `a`+".0"
|
||||
`result` = `a`+".0";
|
||||
}
|
||||
}
|
||||
""".}
|
||||
|
||||
@@ -9,7 +9,7 @@ export osseps
|
||||
import std/envvars
|
||||
import std/private/osappdirs
|
||||
|
||||
import std/pathnorm
|
||||
import std/[pathnorm, hashes, sugar, strutils]
|
||||
|
||||
from std/private/ospaths2 import joinPath, splitPath,
|
||||
ReadDirEffect, WriteDirEffect,
|
||||
@@ -25,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.
|
||||
##
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -763,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
|
||||
|
||||
@@ -66,11 +66,13 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} =
|
||||
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
|
||||
|
||||
@@ -874,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:
|
||||
|
||||
@@ -110,6 +110,19 @@ 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:
|
||||
@@ -121,11 +134,14 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC
|
||||
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:
|
||||
@@ -209,7 +225,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC
|
||||
let funcCall = newCall(e[0], callNode)
|
||||
functionStmtList.add tempAssignList
|
||||
|
||||
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:
|
||||
@@ -241,7 +257,7 @@ 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)
|
||||
|
||||
if returnsVoid:
|
||||
result = quote do:
|
||||
|
||||
@@ -14,7 +14,7 @@ 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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -1037,7 +1037,7 @@ const
|
||||
## Possible values:
|
||||
## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`,
|
||||
## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`,
|
||||
## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, '"loongarch64"'.
|
||||
## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, `"loongarch64"`.
|
||||
|
||||
seqShallowFlag = low(int)
|
||||
strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \
|
||||
@@ -2102,12 +2102,14 @@ when not defined(js):
|
||||
proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
|
||||
## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be
|
||||
## of length `len`.
|
||||
if a == nil: return @[]
|
||||
newSeq(result, len)
|
||||
for i in 0..len-1: result[i] = $a[i]
|
||||
|
||||
proc cstringArrayToSeq*(a: cstringArray): seq[string] =
|
||||
## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be
|
||||
## terminated by `nil`.
|
||||
if a == nil: return @[]
|
||||
var L = 0
|
||||
while a[L] != nil: inc(L)
|
||||
result = cstringArrayToSeq(a, L)
|
||||
@@ -2788,6 +2790,18 @@ when not defined(js):
|
||||
|
||||
proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {.
|
||||
magic: "Slice".}
|
||||
## Allows passing the slice of `x` from the element at `first` to the element
|
||||
## at `last` to `openArray[T]` parameters without copying it.
|
||||
##
|
||||
## Example:
|
||||
## ```nim
|
||||
## proc test(x: openArray[int]) =
|
||||
## doAssert x == [1, 2, 3]
|
||||
##
|
||||
## let s = @[0, 1, 2, 3, 4]
|
||||
## s.toOpenArray(1, 3).test
|
||||
## ```
|
||||
|
||||
proc toOpenArray*[T](x: openArray[T]; first, last: int): openArray[T] {.
|
||||
magic: "Slice".}
|
||||
proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T] {.
|
||||
|
||||
@@ -20,6 +20,37 @@ template track(op, address, size) =
|
||||
|
||||
# We manage *chunks* of memory. Each chunk is a multiple of the page size.
|
||||
# Each chunk starts at an address that is divisible by the page size.
|
||||
# Small chunks may be divided into smaller cells of reusable pointers to reduce the number of page allocations.
|
||||
|
||||
# An allocation of a small pointer looks approximately like this
|
||||
#[
|
||||
|
||||
alloc -> rawAlloc -> No free chunk available > Request a new page from tslf -> result = chunk.data -------------+
|
||||
| |
|
||||
v |
|
||||
Free chunk available |
|
||||
| |
|
||||
v v
|
||||
Fetch shared cells -> No free cells available -> Advance acc -> result = chunk.data + chunk.acc -------> return
|
||||
(may not add new cells) ^
|
||||
| |
|
||||
v |
|
||||
Free cells available -> result = chunk.freeList -> Advance chunk.freeList -----------------------------------+
|
||||
]#
|
||||
# so it is split into 3 paths, where the last path is preferred to prevent unnecessary allocations.
|
||||
#
|
||||
#
|
||||
# A deallocation of a small pointer then looks like this
|
||||
#[
|
||||
dealloc -> rawDealloc -> chunk.owner == addr(a) --------------> This thread owns the chunk ------> The current chunk is active -> Chunk is completely unused -----> Chunk references no foreign cells
|
||||
| | (Add cell into the current chunk) | Return the current chunk back to tlsf
|
||||
| | | |
|
||||
v v v v
|
||||
A different thread owns this chunk. The current chunk is not active. chunk.free was < size Chunk references foreign cells, noop
|
||||
Add the cell to a.sharedFreeLists Add the cell into the active chunk Activate the chunk (end)
|
||||
(end) (end) (end)
|
||||
]#
|
||||
# So "true" deallocation is delayed for as long as possible in favor of reusing cells.
|
||||
|
||||
const
|
||||
nimMinHeapPages {.intdefine.} = 128 # 0.5 MB
|
||||
@@ -71,6 +102,8 @@ const
|
||||
|
||||
type
|
||||
FreeCell {.final, pure.} = object
|
||||
# A free cell is a pointer that has been freed, meaning it became available for reuse.
|
||||
# It may become foreign if it is lent to a chunk that did not create it, doing so reduces the amount of needed pages.
|
||||
next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
|
||||
when not defined(gcDestructors):
|
||||
zeroField: int # 0 means cell is not used (overlaid with typ field)
|
||||
@@ -90,11 +123,18 @@ type
|
||||
|
||||
SmallChunk = object of BaseChunk
|
||||
next, prev: PSmallChunk # chunks of the same size
|
||||
freeList: ptr FreeCell
|
||||
free: int # how many bytes remain
|
||||
acc: int # accumulator for small object allocation
|
||||
when defined(gcDestructors):
|
||||
sharedFreeList: ptr FreeCell # make no attempt at avoiding false sharing for now for this object field
|
||||
freeList: ptr FreeCell # Singly linked list of cells. They may be from foreign chunks or from the current chunk.
|
||||
# Should be `nil` when the chunk isn't active in `a.freeSmallChunks`.
|
||||
free: int32 # Bytes this chunk is able to provide using both the accumulator and free cells.
|
||||
# When a cell is considered foreign, its source chunk's free field is NOT adjusted until it
|
||||
# reaches dealloc while the source chunk is active.
|
||||
# Instead, the receiving chunk gains the capacity and thus reserves space in the foreign chunk.
|
||||
acc: uint32 # Offset from data, used when there are no free cells available but the chunk is considered free.
|
||||
foreignCells: int # When a free cell is given to a chunk that is not its origin,
|
||||
# both the cell and the source chunk are considered foreign.
|
||||
# Receiving a foreign cell can happen both when deallocating from another thread or when
|
||||
# the active chunk in `a.freeSmallChunks` is not the current chunk.
|
||||
# Freeing a chunk while `foreignCells > 0` leaks memory as all references to it become lost.
|
||||
data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
|
||||
|
||||
BigChunk = object of BaseChunk # not necessarily > PageSize!
|
||||
@@ -109,7 +149,12 @@ type
|
||||
MemRegion = object
|
||||
when not defined(gcDestructors):
|
||||
minLargeObj, maxLargeObj: int
|
||||
freeSmallChunks: array[0..max(1,SmallChunkSize div MemAlign-1), PSmallChunk]
|
||||
freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
|
||||
# List of available chunks per size class. Only one is expected to be active per class.
|
||||
when defined(gcDestructors):
|
||||
sharedFreeLists: array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell]
|
||||
# When a thread frees a pointer it did not create, it must not adjust the counters.
|
||||
# Instead, the cell is placed here and deferred until the next allocation.
|
||||
flBitmap: uint32
|
||||
slBitmap: array[RealFli, uint32]
|
||||
matrix: array[RealFli, array[MaxSli, PBigChunk]]
|
||||
@@ -433,7 +478,7 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} =
|
||||
|
||||
let size = c.size
|
||||
var a = cast[int](addr(c.data))
|
||||
let limit = a + c.acc
|
||||
let limit = a + c.acc.int
|
||||
while a <% limit:
|
||||
yield cast[pointer](a)
|
||||
a = a +% size
|
||||
@@ -777,41 +822,42 @@ when defined(gcDestructors):
|
||||
sysAssert c.next == nil, "c.next pointer must be nil"
|
||||
atomicPrepend a.sharedFreeListBigChunks, c
|
||||
|
||||
proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell) {.inline.} =
|
||||
atomicPrepend c.sharedFreeList, f
|
||||
proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell; size: int) {.inline.} =
|
||||
atomicPrepend c.owner.sharedFreeLists[size], f
|
||||
|
||||
const MaxSteps = 20
|
||||
|
||||
proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) =
|
||||
# rawDealloc did NOT do the usual:
|
||||
# `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these
|
||||
# memory locations. We have to compensate here for these for the entire list.
|
||||
# Well, not for the entire list, but for `max` elements of the list because
|
||||
# we split the list in order to achieve bounded response times.
|
||||
var it = c.freeList
|
||||
var x = 0
|
||||
var maxIters = 20 # make it time-bounded
|
||||
var total = 0
|
||||
while it != nil:
|
||||
if maxIters == 0:
|
||||
let rest = it.next.loada
|
||||
if rest != nil:
|
||||
it.next.storea nil
|
||||
addToSharedFreeList(c, rest)
|
||||
break
|
||||
inc x, size
|
||||
it = it.next.loada
|
||||
dec maxIters
|
||||
inc(c.free, x)
|
||||
dec(a.occ, x)
|
||||
inc total, size
|
||||
let chunk = cast[PSmallChunk](pageAddr(it))
|
||||
if c != chunk:
|
||||
# The cell is foreign, potentially even from a foreign thread.
|
||||
# It must block the current chunk from being freed, as doing so would leak memory.
|
||||
inc c.foreignCells
|
||||
it = it.next
|
||||
# By not adjusting the foreign chunk we reserve space in it to prevent deallocation
|
||||
inc(c.free, total)
|
||||
dec(a.occ, total)
|
||||
|
||||
proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) =
|
||||
var it = root
|
||||
var maxIters = 20 # make it time-bounded
|
||||
var maxIters = MaxSteps # make it time-bounded
|
||||
while true:
|
||||
let rest = it.next.loada
|
||||
it.next.storea nil
|
||||
deallocBigChunk(a, cast[PBigChunk](it))
|
||||
if maxIters == 0:
|
||||
let rest = it.next.loada
|
||||
it.next.storea nil
|
||||
addToSharedFreeListBigChunks(a, rest)
|
||||
if rest != nil:
|
||||
addToSharedFreeListBigChunks(a, rest)
|
||||
sysAssert a.sharedFreeListBigChunks != nil, "re-enqueing failed"
|
||||
break
|
||||
it = it.next.loada
|
||||
it = rest
|
||||
dec maxIters
|
||||
if it == nil: break
|
||||
|
||||
@@ -826,56 +872,85 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
|
||||
|
||||
if size <= SmallChunkSize-smallChunkOverhead():
|
||||
template fetchSharedCells(tc: PSmallChunk) =
|
||||
# Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]`
|
||||
when defined(gcDestructors):
|
||||
if tc.freeList == nil:
|
||||
when hasThreadSupport:
|
||||
# Steal the entire list from `sharedFreeList`:
|
||||
tc.freeList = atomicExchangeN(addr a.sharedFreeLists[s], nil, ATOMIC_RELAXED)
|
||||
else:
|
||||
tc.freeList = a.sharedFreeLists[s]
|
||||
a.sharedFreeLists[s] = nil
|
||||
# if `tc.freeList` isn't nil, `tc` will gain capacity.
|
||||
# We must calculate how much it gained and how many foreign cells are included.
|
||||
compensateCounters(a, tc, size)
|
||||
|
||||
# allocate a small block: for small chunks, we use only its next pointer
|
||||
let s = size div MemAlign
|
||||
var c = a.freeSmallChunks[s]
|
||||
if c == nil:
|
||||
# There is no free chunk of the requested size available, we need a new one.
|
||||
c = getSmallChunk(a)
|
||||
# init all fields in case memory didn't get zeroed
|
||||
c.freeList = nil
|
||||
c.foreignCells = 0
|
||||
sysAssert c.size == PageSize, "rawAlloc 3"
|
||||
c.size = size
|
||||
c.acc = size
|
||||
when defined(gcDestructors):
|
||||
c.sharedFreeList = nil
|
||||
c.free = SmallChunkSize - smallChunkOverhead() - size
|
||||
c.acc = size.uint32
|
||||
c.free = SmallChunkSize - smallChunkOverhead() - size.int32
|
||||
sysAssert c.owner == addr(a), "rawAlloc: No owner set!"
|
||||
c.next = nil
|
||||
c.prev = nil
|
||||
listAdd(a.freeSmallChunks[s], c)
|
||||
# Shared cells are fetched here in case `c.size * 2 >= SmallChunkSize - smallChunkOverhead()`.
|
||||
# For those single cell chunks, we would otherwise have to allocate a new one almost every time.
|
||||
fetchSharedCells(c)
|
||||
if c.free >= size:
|
||||
# Because removals from `a.freeSmallChunks[s]` only happen in the other alloc branch and during dealloc,
|
||||
# we must not add it to the list if it cannot be used the next time a pointer of `size` bytes is needed.
|
||||
listAdd(a.freeSmallChunks[s], c)
|
||||
result = addr(c.data)
|
||||
sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4")
|
||||
else:
|
||||
# There is a free chunk of the requested size available, use it.
|
||||
sysAssert(allocInv(a), "rawAlloc: begin c != nil")
|
||||
sysAssert c.next != c, "rawAlloc 5"
|
||||
#if c.size != size:
|
||||
# c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size)
|
||||
sysAssert c.size == size, "rawAlloc 6"
|
||||
when defined(gcDestructors):
|
||||
if c.freeList == nil:
|
||||
when hasThreadSupport:
|
||||
c.freeList = atomicExchangeN(addr c.sharedFreeList, nil, ATOMIC_RELAXED)
|
||||
else:
|
||||
c.freeList = c.sharedFreeList
|
||||
c.sharedFreeList = nil
|
||||
compensateCounters(a, c, size)
|
||||
if c.freeList == nil:
|
||||
sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
|
||||
sysAssert(c.acc.int + smallChunkOverhead() + size <= SmallChunkSize,
|
||||
"rawAlloc 7")
|
||||
result = cast[pointer](cast[int](addr(c.data)) +% c.acc)
|
||||
result = cast[pointer](cast[int](addr(c.data)) +% c.acc.int)
|
||||
inc(c.acc, size)
|
||||
else:
|
||||
# There are free cells available, prefer them over the accumulator
|
||||
result = c.freeList
|
||||
when not defined(gcDestructors):
|
||||
sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
|
||||
c.freeList = c.freeList.next
|
||||
if cast[PSmallChunk](pageAddr(result)) != c:
|
||||
# This cell isn't a blocker for the current chunk's deallocation anymore
|
||||
dec(c.foreignCells)
|
||||
else:
|
||||
sysAssert(c == cast[PSmallChunk](pageAddr(result)), "rawAlloc: Bad cell")
|
||||
# Even if the cell we return is foreign, the local chunk's capacity decreases.
|
||||
# The capacity was previously reserved in the source chunk (when it first got allocated),
|
||||
# then added into the current chunk during dealloc,
|
||||
# so the source chunk will not be freed or leak memory because of this.
|
||||
dec(c.free, size)
|
||||
sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9")
|
||||
sysAssert(allocInv(a), "rawAlloc: end c != nil")
|
||||
sysAssert(allocInv(a), "rawAlloc: before c.free < size")
|
||||
if c.free < size:
|
||||
sysAssert(allocInv(a), "rawAlloc: before listRemove test")
|
||||
listRemove(a.freeSmallChunks[s], c)
|
||||
sysAssert(allocInv(a), "rawAlloc: end listRemove test")
|
||||
# We fetch deferred cells *after* advancing `c.freeList`/`acc` to adjust `c.free`.
|
||||
# If after the adjustment it turns out there's free cells available,
|
||||
# the chunk stays in `a.freeSmallChunks[s]` and the need for a new chunk is delayed.
|
||||
fetchSharedCells(c)
|
||||
sysAssert(allocInv(a), "rawAlloc: before c.free < size")
|
||||
if c.free < size:
|
||||
# Even after fetching shared cells the chunk has no usable memory left. It is no longer the active chunk
|
||||
sysAssert(allocInv(a), "rawAlloc: before listRemove test")
|
||||
listRemove(a.freeSmallChunks[s], c)
|
||||
sysAssert(allocInv(a), "rawAlloc: end listRemove test")
|
||||
sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %%
|
||||
size == 0, "rawAlloc 21")
|
||||
sysAssert(allocInv(a), "rawAlloc: end small size")
|
||||
@@ -907,7 +982,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
trackSize(c.size)
|
||||
sysAssert(isAccessible(a, result), "rawAlloc 14")
|
||||
sysAssert(allocInv(a), "rawAlloc: end")
|
||||
when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize)
|
||||
when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a)
|
||||
|
||||
proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
|
||||
result = rawAlloc(a, requestedSize)
|
||||
@@ -923,7 +998,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
if isSmallChunk(c):
|
||||
# `p` is within a small chunk:
|
||||
var c = cast[PSmallChunk](c)
|
||||
var s = c.size
|
||||
let s = c.size
|
||||
# ^ We might access thread foreign storage here.
|
||||
# The other thread cannot possibly free this block as it's still alive.
|
||||
var f = cast[ptr FreeCell](p)
|
||||
@@ -938,31 +1013,48 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
#echo("setting to nil: ", $cast[int](addr(f.zeroField)))
|
||||
sysAssert(f.zeroField != 0, "rawDealloc 1")
|
||||
f.zeroField = 0
|
||||
f.next = c.freeList
|
||||
c.freeList = f
|
||||
when overwriteFree:
|
||||
# set to 0xff to check for usage after free bugs:
|
||||
nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
|
||||
s -% sizeof(FreeCell))
|
||||
# check if it is not in the freeSmallChunks[s] list:
|
||||
if c.free < s:
|
||||
# add it to the freeSmallChunks[s] array:
|
||||
listAdd(a.freeSmallChunks[s div MemAlign], c)
|
||||
inc(c.free, s)
|
||||
let activeChunk = a.freeSmallChunks[s div MemAlign]
|
||||
if activeChunk != nil and c != activeChunk:
|
||||
# This pointer is not part of the active chunk, lend it out
|
||||
# and do not adjust the current chunk (same logic as compensateCounters.)
|
||||
# Put the cell into the active chunk,
|
||||
# may prevent a queue of available chunks from forming in a.freeSmallChunks[s div MemAlign].
|
||||
# This queue would otherwise waste memory in the form of free cells until we return to those chunks.
|
||||
f.next = activeChunk.freeList
|
||||
activeChunk.freeList = f # lend the cell
|
||||
inc(activeChunk.free, s) # By not adjusting the current chunk's capacity it is prevented from being freed
|
||||
inc(activeChunk.foreignCells) # The cell is now considered foreign from the perspective of the active chunk
|
||||
else:
|
||||
inc(c.free, s)
|
||||
if c.free == SmallChunkSize-smallChunkOverhead():
|
||||
listRemove(a.freeSmallChunks[s div MemAlign], c)
|
||||
c.size = SmallChunkSize
|
||||
freeBigChunk(a, cast[PBigChunk](c))
|
||||
f.next = c.freeList
|
||||
c.freeList = f
|
||||
if c.free < s:
|
||||
# The chunk could not have been active as it didn't have enough space to give
|
||||
listAdd(a.freeSmallChunks[s div MemAlign], c)
|
||||
inc(c.free, s)
|
||||
else:
|
||||
inc(c.free, s)
|
||||
# Free only if the entire chunk is unused and there are no borrowed cells.
|
||||
# If the chunk were to be freed while it references foreign cells,
|
||||
# the foreign chunks will leak memory and can never be freed.
|
||||
if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
|
||||
listRemove(a.freeSmallChunks[s div MemAlign], c)
|
||||
c.size = SmallChunkSize
|
||||
freeBigChunk(a, cast[PBigChunk](c))
|
||||
else:
|
||||
when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))
|
||||
|
||||
when defined(gcDestructors):
|
||||
addToSharedFreeList(c, f)
|
||||
addToSharedFreeList(c, f, s div MemAlign)
|
||||
sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
|
||||
s == 0, "rawDealloc 2")
|
||||
else:
|
||||
# set to 0xff to check for usage after free bugs:
|
||||
when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
|
||||
when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner)
|
||||
when defined(gcDestructors):
|
||||
if c.owner == addr(a):
|
||||
deallocBigChunk(a, cast[PBigChunk](c))
|
||||
@@ -970,8 +1062,9 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c))
|
||||
else:
|
||||
deallocBigChunk(a, cast[PBigChunk](c))
|
||||
|
||||
sysAssert(allocInv(a), "rawDealloc: end")
|
||||
when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
|
||||
#when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
|
||||
|
||||
when not defined(gcDestructors):
|
||||
proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
|
||||
@@ -982,7 +1075,7 @@ when not defined(gcDestructors):
|
||||
var c = cast[PSmallChunk](c)
|
||||
var offset = (cast[int](p) and (PageSize-1)) -%
|
||||
smallChunkOverhead()
|
||||
result = (c.acc >% offset) and (offset %% c.size == 0) and
|
||||
result = (c.acc.int >% offset) and (offset %% c.size == 0) and
|
||||
(cast[ptr FreeCell](p).zeroField >% 1)
|
||||
else:
|
||||
var c = cast[PBigChunk](c)
|
||||
@@ -1000,7 +1093,7 @@ when not defined(gcDestructors):
|
||||
var c = cast[PSmallChunk](c)
|
||||
var offset = (cast[int](p) and (PageSize-1)) -%
|
||||
smallChunkOverhead()
|
||||
if c.acc >% offset:
|
||||
if c.acc.int >% offset:
|
||||
sysAssert(cast[int](addr(c.data)) +% offset ==
|
||||
cast[int](p), "offset is not what you think it is")
|
||||
var d = cast[ptr FreeCell](cast[int](addr(c.data)) +%
|
||||
|
||||
@@ -324,7 +324,7 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
|
||||
return true
|
||||
else:
|
||||
var sameObject = false
|
||||
{.emit: """`sameObject` = `x` === `y`""".}
|
||||
{.emit: """`sameObject` = `x` === `y`;""".}
|
||||
if sameObject: return true
|
||||
|
||||
if x.len != y.len:
|
||||
|
||||
@@ -10,7 +10,7 @@ const
|
||||
## is the minor number of Nim's version.
|
||||
## Odd for devel, even for releases.
|
||||
|
||||
NimPatch* {.intdefine.}: int = 1
|
||||
NimPatch* {.intdefine.}: int = 9
|
||||
## is the patch number of Nim's version.
|
||||
## Odd for devel, even for releases.
|
||||
|
||||
|
||||
@@ -80,8 +80,6 @@ proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline, systemRa
|
||||
## var s = "abcdef"
|
||||
## assert s[1..3] == "bcd"
|
||||
## ```
|
||||
# Workaround bug #22852
|
||||
result = ""
|
||||
let a = s ^^ x.a
|
||||
let L = (s ^^ x.b) - a + 1
|
||||
result = newString(L)
|
||||
@@ -112,13 +110,13 @@ proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.s
|
||||
## var a = [1, 2, 3, 4]
|
||||
## assert a[0..2] == @[1, 2, 3]
|
||||
## ```
|
||||
##
|
||||
## See also:
|
||||
## * `toOpenArray(array[I, T];I,I) <#toOpenArray,array[I,T],I,I>`_
|
||||
let xa = a ^^ x.a
|
||||
let L = (a ^^ x.b) - xa + 1
|
||||
# Workaround bug #22852:
|
||||
result = newSeq[T](if L < 0: 0 else: L)
|
||||
result = newSeq[T](L)
|
||||
for i in 0..<L: result[i] = a[Idx(i + xa)]
|
||||
# Workaround bug #22852
|
||||
discard Natural(L)
|
||||
|
||||
proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} =
|
||||
## Slice assignment for arrays.
|
||||
@@ -141,6 +139,9 @@ proc `[]`*[T; U, V: Ordinal](s: openArray[T], x: HSlice[U, V]): seq[T] {.systemR
|
||||
## var s = @[1, 2, 3, 4]
|
||||
## assert s[0..2] == @[1, 2, 3]
|
||||
## ```
|
||||
##
|
||||
## See also:
|
||||
## * `toOpenArray(openArray[T];int,int) <#toOpenArray,openArray[T],int,int>`_
|
||||
let a = s ^^ x.a
|
||||
let L = (s ^^ x.b) - a + 1
|
||||
newSeq(result, L)
|
||||
|
||||
@@ -622,37 +622,6 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
|
||||
else:
|
||||
result = src
|
||||
|
||||
proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} =
|
||||
{.emit: "`result` = null;".}
|
||||
case ti.kind
|
||||
of tyPtr, tyRef, tyVar, tyNil:
|
||||
if isFatPointer(ti):
|
||||
{.emit: """
|
||||
`result` = [null, 0];
|
||||
""".}
|
||||
of tySet:
|
||||
{.emit: """
|
||||
`result` = {};
|
||||
""".}
|
||||
of tyTuple, tyObject:
|
||||
if ti.kind == tyObject:
|
||||
{.emit: "`result` = {m_type: `ti`};".}
|
||||
else:
|
||||
{.emit: "`result` = {};".}
|
||||
of tySequence, tyOpenArray, tyString:
|
||||
{.emit: """
|
||||
`result` = [];
|
||||
""".}
|
||||
of tyArrayConstr, tyArray:
|
||||
{.emit: """
|
||||
`result` = new Array(`x`.length);
|
||||
for (var i = 0; i < `x`.length; ++i) {
|
||||
`result`[i] = genericReset(`x`[i], `ti`.base);
|
||||
}
|
||||
""".}
|
||||
else:
|
||||
discard
|
||||
|
||||
proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
|
||||
asmNoStackFrame, compilerproc.} =
|
||||
# types are fake
|
||||
|
||||
@@ -253,11 +253,12 @@ proc cpDir*(`from`, to: string) {.raises: [OSError].} =
|
||||
proc exec*(command: string) {.
|
||||
raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
|
||||
## Executes an external process. If the external process terminates with
|
||||
## a non-zero exit code, an OSError exception is raised.
|
||||
## a non-zero exit code, an OSError exception is raised. The command is
|
||||
## executed relative to the current source path.
|
||||
##
|
||||
## **Note:** If you need a version of `exec` that returns the exit code
|
||||
## and text output of the command, you can use `system.gorgeEx
|
||||
## <system.html#gorgeEx,string,string,string>`_.
|
||||
## .. note:: If you need a version of `exec` that returns the exit code
|
||||
## and text output of the command, you can use `system.gorgeEx
|
||||
## <system.html#gorgeEx,string,string,string>`_.
|
||||
log "exec: " & command:
|
||||
if rawExec(command) != 0:
|
||||
raise newException(OSError, "FAILED: " & command)
|
||||
@@ -267,11 +268,17 @@ proc exec*(command: string, input: string, cache = "") {.
|
||||
raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
|
||||
## Executes an external process. If the external process terminates with
|
||||
## a non-zero exit code, an OSError exception is raised.
|
||||
##
|
||||
## .. warning:: This version of `exec` is executed relative to the nimscript
|
||||
## module path, which affects how the command resolves relative paths. Thus
|
||||
## it is generally better to use `gorgeEx` directly when you need more
|
||||
## control over the execution environment or when working with commands
|
||||
## that deal with relative paths.
|
||||
log "exec: " & command:
|
||||
let (output, exitCode) = gorgeEx(command, input, cache)
|
||||
echo output
|
||||
if exitCode != 0:
|
||||
raise newException(OSError, "FAILED: " & command)
|
||||
echo output
|
||||
|
||||
proc selfExec*(command: string) {.
|
||||
raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
|
||||
|
||||
@@ -146,7 +146,7 @@ proc unregisterCycle(s: Cell) =
|
||||
let idx = s.rootIdx-1
|
||||
when false:
|
||||
if idx >= roots.len or idx < 0:
|
||||
cprintf("[Bug!] %ld\n", idx)
|
||||
cprintf("[Bug!] %ld %ld\n", idx, roots.len)
|
||||
rawQuit 1
|
||||
roots.d[idx] = roots.d[roots.len-1]
|
||||
roots.d[idx][0].rootIdx = idx+1
|
||||
@@ -303,6 +303,14 @@ proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) =
|
||||
t.setColor(colBlack)
|
||||
trace(t, desc, j)
|
||||
|
||||
const
|
||||
defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
|
||||
|
||||
when defined(nimStressOrc):
|
||||
const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
|
||||
else:
|
||||
var rootsThreshold {.threadvar.}: int
|
||||
|
||||
proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
|
||||
# pretty direct translation from
|
||||
# https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf
|
||||
@@ -341,22 +349,25 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
|
||||
s.rootIdx = 0
|
||||
collectColor(s, roots.d[i][1], colToCollect, j)
|
||||
|
||||
# Bug #22927: `free` calls destructors which can append to `roots`.
|
||||
# We protect against this here by setting `roots.len` to 0 and also
|
||||
# setting the threshold so high that no cycle collection can be triggered
|
||||
# until we are out of this critical section:
|
||||
when not defined(nimStressOrc):
|
||||
let oldThreshold = rootsThreshold
|
||||
rootsThreshold = high(int)
|
||||
roots.len = 0
|
||||
|
||||
for i in 0 ..< j.toFree.len:
|
||||
when orcLeakDetector:
|
||||
writeCell("CYCLIC OBJECT FREED", j.toFree.d[i][0], j.toFree.d[i][1])
|
||||
free(j.toFree.d[i][0], j.toFree.d[i][1])
|
||||
|
||||
when not defined(nimStressOrc):
|
||||
rootsThreshold = oldThreshold
|
||||
|
||||
inc j.freed, j.toFree.len
|
||||
deinit j.toFree
|
||||
#roots.len = 0
|
||||
|
||||
const
|
||||
defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
|
||||
|
||||
when defined(nimStressOrc):
|
||||
const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
|
||||
else:
|
||||
var rootsThreshold {.threadvar.}: int
|
||||
|
||||
when defined(nimOrcStats):
|
||||
var freedCyclicObjects {.threadvar.}: int
|
||||
@@ -396,7 +407,8 @@ proc collectCycles() =
|
||||
collectCyclesBacon(j, 0)
|
||||
|
||||
deinit j.traceStack
|
||||
deinit roots
|
||||
if roots.len == 0:
|
||||
deinit roots
|
||||
|
||||
when not defined(nimStressOrc):
|
||||
# compute the threshold based on the previous history
|
||||
|
||||
@@ -29,7 +29,7 @@ proc reprBool(x: bool): string {.compilerRtl.} =
|
||||
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
|
||||
var tmp: bool
|
||||
let item = typ.node.sons[e]
|
||||
{.emit: "`tmp` = `item` !== undefined".}
|
||||
{.emit: "`tmp` = `item` !== undefined;".}
|
||||
if tmp:
|
||||
result = makeNimstrLit(item.name)
|
||||
else:
|
||||
@@ -136,7 +136,7 @@ proc reprArray(a: pointer, typ: PNimType,
|
||||
add(result, "]")
|
||||
|
||||
proc isPointedToNil(p: pointer): bool =
|
||||
{. emit: "if (`p` === null) {`result` = true};\n" .}
|
||||
{. emit: "if (`p` === null) {`result` = true;}\n" .}
|
||||
|
||||
proc reprRef(result: var string, p: pointer, typ: PNimType,
|
||||
cl: var ReprClosure) =
|
||||
|
||||
@@ -90,6 +90,12 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin
|
||||
q.cap = newCap
|
||||
result = q
|
||||
|
||||
proc zeroNewElements(len: int; q: pointer; addlen, elemSize, elemAlign: int) {.
|
||||
noSideEffect, tags: [], raises: [], compilerRtl.} =
|
||||
{.noSideEffect.}:
|
||||
let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
|
||||
zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
|
||||
|
||||
proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
|
||||
noSideEffect, tags: [], raises: [], compilerRtl.} =
|
||||
{.noSideEffect.}:
|
||||
|
||||
@@ -220,7 +220,10 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
|
||||
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
|
||||
let n = max(newLen, 0)
|
||||
if s == nil:
|
||||
result = mnewString(n)
|
||||
if n == 0:
|
||||
return s
|
||||
else:
|
||||
result = mnewString(n)
|
||||
elif n <= s.space:
|
||||
result = s
|
||||
else:
|
||||
@@ -301,7 +304,10 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
|
||||
compilerRtl.} =
|
||||
sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
|
||||
if s == nil:
|
||||
result = cast[PGenericSeq](newSeq(typ, newLen))
|
||||
if newLen == 0:
|
||||
result = s
|
||||
else:
|
||||
result = cast[PGenericSeq](newSeq(typ, newLen))
|
||||
else:
|
||||
let elemSize = typ.base.size
|
||||
let elemAlign = typ.base.align
|
||||
|
||||
Reference in New Issue
Block a user