mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
refactorings in preparations for the new runtime
This commit is contained in:
@@ -164,7 +164,7 @@ proc canMove(n: PNode): bool =
|
||||
# result = false
|
||||
|
||||
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
if dest.storage == OnStack or not usesNativeGC(p.config):
|
||||
if dest.storage == OnStack or not usesWriteBarrier(p.config):
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
elif dest.storage == OnHeap:
|
||||
# location is on heap
|
||||
@@ -256,7 +256,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# (for objects, etc.):
|
||||
if needToCopy notin flags or
|
||||
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
|
||||
if dest.storage == OnStack or not usesNativeGC(p.config):
|
||||
if dest.storage == OnStack or not usesWriteBarrier(p.config):
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts,
|
||||
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
@@ -290,7 +290,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
|
||||
genRefAssign(p, dest, src, flags)
|
||||
else:
|
||||
if dest.storage == OnStack or not usesNativeGC(p.config):
|
||||
if dest.storage == OnStack or not usesWriteBarrier(p.config):
|
||||
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
|
||||
elif dest.storage == OnHeap:
|
||||
# we use a temporary to care for the dreaded self assignment:
|
||||
@@ -1150,7 +1150,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
|
||||
addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
|
||||
|
||||
let args = [getTypeDesc(p.module, typ), ti, sizeExpr]
|
||||
if a.storage == OnHeap and usesNativeGC(p.config):
|
||||
if a.storage == OnHeap and usesWriteBarrier(p.config):
|
||||
# use newObjRC1 as an optimization
|
||||
if canFormAcycle(a.t):
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
|
||||
@@ -1181,7 +1181,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
|
||||
genTypeInfo(p.module, seqtype, dest.lode.info), length]
|
||||
var call: TLoc
|
||||
initLoc(call, locExpr, dest.lode, OnHeap)
|
||||
if dest.storage == OnHeap and usesNativeGC(p.config):
|
||||
if dest.storage == OnHeap and usesWriteBarrier(p.config):
|
||||
if canFormAcycle(dest.t):
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
|
||||
else:
|
||||
|
||||
@@ -16,7 +16,7 @@ const
|
||||
# above X strings a hash-switch for strings is generated
|
||||
|
||||
proc registerGcRoot(p: BProc, v: PSym) =
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and
|
||||
containsGarbageCollectedRef(v.loc.t):
|
||||
# we register a specialized marked proc here; this has the advantage
|
||||
# that it works out of the box for thread local storage then :-)
|
||||
|
||||
@@ -215,7 +215,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
of "refc": result = conf.selectedGC == gcRefc
|
||||
of "v2": result = conf.selectedGC == gcV2
|
||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||
of "generational": result = conf.selectedGC == gcGenerational
|
||||
of "generational": result = false
|
||||
of "destructors": result = conf.selectedGC == gcDestructors
|
||||
of "go": result = conf.selectedGC == gcGo
|
||||
of "none": result = conf.selectedGC == gcNone
|
||||
of "stack", "regions": result = conf.selectedGC == gcRegions
|
||||
@@ -435,9 +436,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "markandsweep":
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
of "generational":
|
||||
conf.selectedGC = gcGenerational
|
||||
defineSymbol(conf.symbols, "gcgenerational")
|
||||
of "destructors":
|
||||
conf.selectedGC = gcDestructors
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
of "go":
|
||||
conf.selectedGC = gcGo
|
||||
defineSymbol(conf.symbols, "gogc")
|
||||
|
||||
@@ -103,8 +103,8 @@ type
|
||||
cmdJsonScript # compile a .json build file
|
||||
TStringSeq* = seq[string]
|
||||
TGCMode* = enum # the selected GC
|
||||
gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
|
||||
gcV2, gcGenerational
|
||||
gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcDestructors,
|
||||
gcRefc, gcV2
|
||||
|
||||
IdeCmd* = enum
|
||||
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
|
||||
@@ -368,7 +368,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
|
||||
else: discard
|
||||
|
||||
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
|
||||
proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
|
||||
proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
|
||||
|
||||
template compilationCachePresent*(conf: ConfigRef): untyped =
|
||||
conf.symbolFiles in {v2Sf, writeOnlySf}
|
||||
|
||||
@@ -179,11 +179,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
|
||||
if graph.stopCompile(): break
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
#if {sfNoForward, sfReorder} * module.flags != {}:
|
||||
when true:
|
||||
# we now process the full AST in one go, so that destructor injection for top
|
||||
# level statements works correctly.
|
||||
if graph.stopCompile(): break
|
||||
if {sfNoForward, sfReorder} * module.flags != {}:
|
||||
# read everything, no streaming possible
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
sl.add n
|
||||
@@ -195,7 +191,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
|
||||
sl = reorder(graph, sl, module)
|
||||
discard processTopLevelStmt(sl, a)
|
||||
break
|
||||
#elif not processTopLevelStmt(n, a): break
|
||||
elif not processTopLevelStmt(n, a): break
|
||||
closeParsers(p)
|
||||
if s.kind != llsStdIn: break
|
||||
closePasses(graph, a)
|
||||
|
||||
@@ -3264,6 +3264,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hasAlloc: include "system/sysstr"
|
||||
{.pop.}
|
||||
when hasAlloc: include "system/strmantle"
|
||||
|
||||
when hostOS != "standalone": include "system/sysio"
|
||||
when hasThreadSupport:
|
||||
|
||||
293
lib/system/strmantle.nim
Normal file
293
lib/system/strmantle.nim
Normal file
@@ -0,0 +1,293 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2018 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Compilerprocs for strings that do not depend on the string implementation.
|
||||
|
||||
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
|
||||
if a == b: return 0
|
||||
when defined(nimNoNil):
|
||||
let alen = if a == nil: 0 else: a.len
|
||||
let blen = if b == nil: 0 else: b.len
|
||||
else:
|
||||
if a == nil: return -1
|
||||
if b == nil: return 1
|
||||
let alen = a.len
|
||||
let blen = b.len
|
||||
let minlen = min(alen, blen)
|
||||
if minlen > 0:
|
||||
result = c_memcmp(addr a.data, addr b.data, minlen.csize)
|
||||
if result == 0:
|
||||
result = alen - blen
|
||||
else:
|
||||
result = alen - blen
|
||||
|
||||
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
|
||||
if a == b: return true
|
||||
when defined(nimNoNil):
|
||||
let alen = if a == nil: 0 else: a.len
|
||||
let blen = if b == nil: 0 else: b.len
|
||||
else:
|
||||
if a == nil or b == nil: return false
|
||||
let alen = a.len
|
||||
let blen = b.len
|
||||
if alen == blen:
|
||||
if alen == 0: return true
|
||||
return equalMem(addr(a.data), addr(b.data), alen)
|
||||
|
||||
proc hashString(s: string): int {.compilerproc.} =
|
||||
# the compiler needs exactly the same hash function!
|
||||
# this used to be used for efficient generation of string case statements
|
||||
var h = 0
|
||||
for i in 0..len(s)-1:
|
||||
h = h +% ord(s[i])
|
||||
h = h +% h shl 10
|
||||
h = h xor (h shr 6)
|
||||
h = h +% h shl 3
|
||||
h = h xor (h shr 11)
|
||||
h = h +% h shl 15
|
||||
result = h
|
||||
|
||||
proc add*(result: var string; x: int64) =
|
||||
let base = result.len
|
||||
setLen(result, base + sizeof(x)*4)
|
||||
var i = 0
|
||||
var y = x
|
||||
while true:
|
||||
var d = y div 10
|
||||
result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
|
||||
inc(i)
|
||||
y = d
|
||||
if y == 0: break
|
||||
if x < 0:
|
||||
result[base+i] = '-'
|
||||
inc(i)
|
||||
setLen(result, base+i)
|
||||
# mirror the string:
|
||||
for j in 0..i div 2 - 1:
|
||||
swap(result[base+j], result[base+i-j-1])
|
||||
|
||||
proc nimIntToStr(x: int): string {.compilerRtl.} =
|
||||
result = newStringOfCap(sizeof(x)*4)
|
||||
result.add x
|
||||
|
||||
proc add*(result: var string; x: float) =
|
||||
when nimvm:
|
||||
result.add $x
|
||||
else:
|
||||
var buf: array[0..64, char]
|
||||
when defined(nimNoArrayToCstringConversion):
|
||||
var n: int = c_sprintf(addr buf, "%.16g", x)
|
||||
else:
|
||||
var n: int = c_sprintf(buf, "%.16g", x)
|
||||
var hasDot = false
|
||||
for i in 0..n-1:
|
||||
if buf[i] == ',':
|
||||
buf[i] = '.'
|
||||
hasDot = true
|
||||
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
|
||||
hasDot = true
|
||||
if not hasDot:
|
||||
buf[n] = '.'
|
||||
buf[n+1] = '0'
|
||||
buf[n+2] = '\0'
|
||||
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
|
||||
# of '-1.#IND' are produced.
|
||||
# We want to get rid of these here:
|
||||
if buf[n-1] in {'n', 'N', 'D', 'd'}:
|
||||
result.add "nan"
|
||||
elif buf[n-1] == 'F':
|
||||
if buf[0] == '-':
|
||||
result.add "-inf"
|
||||
else:
|
||||
result.add "inf"
|
||||
else:
|
||||
var i = 0
|
||||
while buf[i] != '\0':
|
||||
result.add buf[i]
|
||||
inc i
|
||||
|
||||
proc nimFloatToStr(f: float): string {.compilerproc.} =
|
||||
result = newStringOfCap(8)
|
||||
result.add f
|
||||
|
||||
proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
|
||||
importc: "strtod", header: "<stdlib.h>", noSideEffect.}
|
||||
|
||||
const
|
||||
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||
powtens = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22]
|
||||
|
||||
proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
||||
start = 0): int {.compilerProc.} =
|
||||
# This routine attempt to parse float that can parsed quickly.
|
||||
# ie whose integer part can fit inside a 53bits integer.
|
||||
# their real exponent must also be <= 22. If the float doesn't follow
|
||||
# these restrictions, transform the float into this form:
|
||||
# INTEGER * 10 ^ exponent and leave the work to standard `strtod()`.
|
||||
# This avoid the problems of decimal character portability.
|
||||
# see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||
var
|
||||
i = start
|
||||
sign = 1.0
|
||||
kdigits, fdigits = 0
|
||||
exponent: int
|
||||
integer: uint64
|
||||
frac_exponent = 0
|
||||
exp_sign = 1
|
||||
first_digit = -1
|
||||
has_sign = false
|
||||
|
||||
# Sign?
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
has_sign = true
|
||||
if s[i] == '-':
|
||||
sign = -1.0
|
||||
inc(i)
|
||||
|
||||
# NaN?
|
||||
if s[i] == 'N' or s[i] == 'n':
|
||||
if s[i+1] == 'A' or s[i+1] == 'a':
|
||||
if s[i+2] == 'N' or s[i+2] == 'n':
|
||||
if s[i+3] notin IdentChars:
|
||||
number = NaN
|
||||
return i+3 - start
|
||||
return 0
|
||||
|
||||
# Inf?
|
||||
if s[i] == 'I' or s[i] == 'i':
|
||||
if s[i+1] == 'N' or s[i+1] == 'n':
|
||||
if s[i+2] == 'F' or s[i+2] == 'f':
|
||||
if s[i+3] notin IdentChars:
|
||||
number = Inf*sign
|
||||
return i+3 - start
|
||||
return 0
|
||||
|
||||
if s[i] in {'0'..'9'}:
|
||||
first_digit = (s[i].ord - '0'.ord)
|
||||
# Integer part?
|
||||
while s[i] in {'0'..'9'}:
|
||||
inc(kdigits)
|
||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
|
||||
# Fractional part?
|
||||
if s[i] == '.':
|
||||
inc(i)
|
||||
# if no integer part, Skip leading zeros
|
||||
if kdigits <= 0:
|
||||
while s[i] == '0':
|
||||
inc(frac_exponent)
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
|
||||
if first_digit == -1 and s[i] in {'0'..'9'}:
|
||||
first_digit = (s[i].ord - '0'.ord)
|
||||
# get fractional part
|
||||
while s[i] in {'0'..'9'}:
|
||||
inc(fdigits)
|
||||
inc(frac_exponent)
|
||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
|
||||
# if has no digits: return error
|
||||
if kdigits + fdigits <= 0 and
|
||||
(i == start or # no char consumed (empty string).
|
||||
(i == start + 1 and has_sign)): # or only '+' or '-
|
||||
return 0
|
||||
|
||||
if s[i] in {'e', 'E'}:
|
||||
inc(i)
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
if s[i] == '-':
|
||||
exp_sign = -1
|
||||
|
||||
inc(i)
|
||||
if s[i] notin {'0'..'9'}:
|
||||
return 0
|
||||
while s[i] in {'0'..'9'}:
|
||||
exponent = exponent * 10 + (ord(s[i]) - ord('0'))
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||
|
||||
var real_exponent = exp_sign*exponent - frac_exponent
|
||||
let exp_negative = real_exponent < 0
|
||||
var abs_exponent = abs(real_exponent)
|
||||
|
||||
# if exponent greater than can be represented: +/- zero or infinity
|
||||
if abs_exponent > 999:
|
||||
if exp_negative:
|
||||
number = 0.0*sign
|
||||
else:
|
||||
number = Inf*sign
|
||||
return i - start
|
||||
|
||||
# if integer is representable in 53 bits: fast path
|
||||
# max fast path integer is 1<<53 - 1 or 8999999999999999 (16 digits)
|
||||
let digits = kdigits + fdigits
|
||||
if digits <= 15 or (digits <= 16 and first_digit <= 8):
|
||||
# max float power of ten with set bits above the 53th bit is 10^22
|
||||
if abs_exponent <= 22:
|
||||
if exp_negative:
|
||||
number = sign * integer.float / powtens[abs_exponent]
|
||||
else:
|
||||
number = sign * integer.float * powtens[abs_exponent]
|
||||
return i - start
|
||||
|
||||
# if exponent is greater try to fit extra exponent above 22 by multiplying
|
||||
# integer part is there is space left.
|
||||
let slop = 15 - kdigits - fdigits
|
||||
if abs_exponent <= 22 + slop and not exp_negative:
|
||||
number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop]
|
||||
return i - start
|
||||
|
||||
# if failed: slow path with strtod.
|
||||
var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
|
||||
var ti = 0
|
||||
let maxlen = t.high - "e+000".len # reserve enough space for exponent
|
||||
|
||||
result = i - start
|
||||
i = start
|
||||
# re-parse without error checking, any error should be handled by the code above.
|
||||
if s[i] == '.': i.inc
|
||||
while s[i] in {'0'..'9','+','-'}:
|
||||
if ti < maxlen:
|
||||
t[ti] = s[i]; inc(ti)
|
||||
inc(i)
|
||||
while s[i] in {'.', '_'}: # skip underscore and decimal point
|
||||
inc(i)
|
||||
|
||||
# insert exponent
|
||||
t[ti] = 'E'; inc(ti)
|
||||
t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
|
||||
inc(ti, 3)
|
||||
|
||||
# insert adjusted exponent
|
||||
t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
|
||||
t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
|
||||
t[ti-3] = ('0'.ord + abs_exponent mod 10).char
|
||||
|
||||
when defined(nimNoArrayToCstringConversion):
|
||||
number = c_strtod(addr t, nil)
|
||||
else:
|
||||
number = c_strtod(t, nil)
|
||||
|
||||
proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
|
||||
result = newStringOfCap(sizeof(x)*4)
|
||||
result.add x
|
||||
|
||||
proc nimBoolToStr(x: bool): string {.compilerRtl.} =
|
||||
return if x: "true" else: "false"
|
||||
|
||||
proc nimCharToStr(x: char): string {.compilerRtl.} =
|
||||
result = newString(1)
|
||||
result[0] = x
|
||||
@@ -20,37 +20,6 @@ proc resize(old: int): int {.inline.} =
|
||||
elif old < 65536: result = old * 2
|
||||
else: result = old * 3 div 2 # for large arrays * 3/2 is better
|
||||
|
||||
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
|
||||
if a == b: return 0
|
||||
when defined(nimNoNil):
|
||||
let alen = if a == nil: 0 else: a.len
|
||||
let blen = if b == nil: 0 else: b.len
|
||||
else:
|
||||
if a == nil: return -1
|
||||
if b == nil: return 1
|
||||
let alen = a.len
|
||||
let blen = b.len
|
||||
let minlen = min(alen, blen)
|
||||
if minlen > 0:
|
||||
result = c_memcmp(addr a.data, addr b.data, minlen.csize)
|
||||
if result == 0:
|
||||
result = alen - blen
|
||||
else:
|
||||
result = alen - blen
|
||||
|
||||
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
|
||||
if a == b: return true
|
||||
when defined(nimNoNil):
|
||||
let alen = if a == nil: 0 else: a.len
|
||||
let blen = if b == nil: 0 else: b.len
|
||||
else:
|
||||
if a == nil or b == nil: return false
|
||||
let alen = a.len
|
||||
let blen = b.len
|
||||
if alen == blen:
|
||||
if alen == 0: return true
|
||||
return equalMem(addr(a.data), addr(b.data), alen)
|
||||
|
||||
when declared(allocAtomic):
|
||||
template allocStr(size: untyped): untyped =
|
||||
cast[NimString](allocAtomic(size))
|
||||
@@ -162,19 +131,6 @@ proc copyDeepString(src: NimString): NimString {.inline.} =
|
||||
result.len = src.len
|
||||
copyMem(addr(result.data), addr(src.data), src.len + 1)
|
||||
|
||||
proc hashString(s: string): int {.compilerproc.} =
|
||||
# the compiler needs exactly the same hash function!
|
||||
# this used to be used for efficient generation of string case statements
|
||||
var h = 0
|
||||
for i in 0..len(s)-1:
|
||||
h = h +% ord(s[i])
|
||||
h = h +% h shl 10
|
||||
h = h xor (h shr 6)
|
||||
h = h +% h shl 3
|
||||
h = h xor (h shr 11)
|
||||
h = h +% h shl 15
|
||||
result = h
|
||||
|
||||
proc addChar(s: NimString, c: char): NimString =
|
||||
# is compilerproc!
|
||||
if s == nil:
|
||||
@@ -246,7 +202,7 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
|
||||
inc(dest.len)
|
||||
|
||||
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
|
||||
var n = max(newLen, 0)
|
||||
let n = max(newLen, 0)
|
||||
if s == nil:
|
||||
result = mnewString(newLen)
|
||||
elif n <= s.space:
|
||||
@@ -340,243 +296,3 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
|
||||
result = cast[PGenericSeq](newSeq(typ, newLen))
|
||||
else:
|
||||
result = setLengthSeq(s, typ.base.size, newLen)
|
||||
|
||||
# --------------- other string routines ----------------------------------
|
||||
proc add*(result: var string; x: int64) =
|
||||
let base = result.len
|
||||
setLen(result, base + sizeof(x)*4)
|
||||
var i = 0
|
||||
var y = x
|
||||
while true:
|
||||
var d = y div 10
|
||||
result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
|
||||
inc(i)
|
||||
y = d
|
||||
if y == 0: break
|
||||
if x < 0:
|
||||
result[base+i] = '-'
|
||||
inc(i)
|
||||
setLen(result, base+i)
|
||||
# mirror the string:
|
||||
for j in 0..i div 2 - 1:
|
||||
swap(result[base+j], result[base+i-j-1])
|
||||
|
||||
proc nimIntToStr(x: int): string {.compilerRtl.} =
|
||||
result = newStringOfCap(sizeof(x)*4)
|
||||
result.add x
|
||||
|
||||
proc add*(result: var string; x: float) =
|
||||
when nimvm:
|
||||
result.add $x
|
||||
else:
|
||||
var buf: array[0..64, char]
|
||||
when defined(nimNoArrayToCstringConversion):
|
||||
var n: int = c_sprintf(addr buf, "%.16g", x)
|
||||
else:
|
||||
var n: int = c_sprintf(buf, "%.16g", x)
|
||||
var hasDot = false
|
||||
for i in 0..n-1:
|
||||
if buf[i] == ',':
|
||||
buf[i] = '.'
|
||||
hasDot = true
|
||||
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
|
||||
hasDot = true
|
||||
if not hasDot:
|
||||
buf[n] = '.'
|
||||
buf[n+1] = '0'
|
||||
buf[n+2] = '\0'
|
||||
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
|
||||
# of '-1.#IND' are produced.
|
||||
# We want to get rid of these here:
|
||||
if buf[n-1] in {'n', 'N', 'D', 'd'}:
|
||||
result.add "nan"
|
||||
elif buf[n-1] == 'F':
|
||||
if buf[0] == '-':
|
||||
result.add "-inf"
|
||||
else:
|
||||
result.add "inf"
|
||||
else:
|
||||
var i = 0
|
||||
while buf[i] != '\0':
|
||||
result.add buf[i]
|
||||
inc i
|
||||
|
||||
proc nimFloatToStr(f: float): string {.compilerproc.} =
|
||||
result = newStringOfCap(8)
|
||||
result.add f
|
||||
|
||||
proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
|
||||
importc: "strtod", header: "<stdlib.h>", noSideEffect.}
|
||||
|
||||
const
|
||||
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||
powtens = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22]
|
||||
|
||||
proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
||||
start = 0): int {.compilerProc.} =
|
||||
# This routine attempt to parse float that can parsed quickly.
|
||||
# ie whose integer part can fit inside a 53bits integer.
|
||||
# their real exponent must also be <= 22. If the float doesn't follow
|
||||
# these restrictions, transform the float into this form:
|
||||
# INTEGER * 10 ^ exponent and leave the work to standard `strtod()`.
|
||||
# This avoid the problems of decimal character portability.
|
||||
# see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||
var
|
||||
i = start
|
||||
sign = 1.0
|
||||
kdigits, fdigits = 0
|
||||
exponent: int
|
||||
integer: uint64
|
||||
frac_exponent = 0
|
||||
exp_sign = 1
|
||||
first_digit = -1
|
||||
has_sign = false
|
||||
|
||||
# Sign?
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
has_sign = true
|
||||
if s[i] == '-':
|
||||
sign = -1.0
|
||||
inc(i)
|
||||
|
||||
# NaN?
|
||||
if s[i] == 'N' or s[i] == 'n':
|
||||
if s[i+1] == 'A' or s[i+1] == 'a':
|
||||
if s[i+2] == 'N' or s[i+2] == 'n':
|
||||
if s[i+3] notin IdentChars:
|
||||
number = NaN
|
||||
return i+3 - start
|
||||
return 0
|
||||
|
||||
# Inf?
|
||||
if s[i] == 'I' or s[i] == 'i':
|
||||
if s[i+1] == 'N' or s[i+1] == 'n':
|
||||
if s[i+2] == 'F' or s[i+2] == 'f':
|
||||
if s[i+3] notin IdentChars:
|
||||
number = Inf*sign
|
||||
return i+3 - start
|
||||
return 0
|
||||
|
||||
if s[i] in {'0'..'9'}:
|
||||
first_digit = (s[i].ord - '0'.ord)
|
||||
# Integer part?
|
||||
while s[i] in {'0'..'9'}:
|
||||
inc(kdigits)
|
||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
|
||||
# Fractional part?
|
||||
if s[i] == '.':
|
||||
inc(i)
|
||||
# if no integer part, Skip leading zeros
|
||||
if kdigits <= 0:
|
||||
while s[i] == '0':
|
||||
inc(frac_exponent)
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
|
||||
if first_digit == -1 and s[i] in {'0'..'9'}:
|
||||
first_digit = (s[i].ord - '0'.ord)
|
||||
# get fractional part
|
||||
while s[i] in {'0'..'9'}:
|
||||
inc(fdigits)
|
||||
inc(frac_exponent)
|
||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
|
||||
# if has no digits: return error
|
||||
if kdigits + fdigits <= 0 and
|
||||
(i == start or # no char consumed (empty string).
|
||||
(i == start + 1 and has_sign)): # or only '+' or '-
|
||||
return 0
|
||||
|
||||
if s[i] in {'e', 'E'}:
|
||||
inc(i)
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
if s[i] == '-':
|
||||
exp_sign = -1
|
||||
|
||||
inc(i)
|
||||
if s[i] notin {'0'..'9'}:
|
||||
return 0
|
||||
while s[i] in {'0'..'9'}:
|
||||
exponent = exponent * 10 + (ord(s[i]) - ord('0'))
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||
|
||||
var real_exponent = exp_sign*exponent - frac_exponent
|
||||
let exp_negative = real_exponent < 0
|
||||
var abs_exponent = abs(real_exponent)
|
||||
|
||||
# if exponent greater than can be represented: +/- zero or infinity
|
||||
if abs_exponent > 999:
|
||||
if exp_negative:
|
||||
number = 0.0*sign
|
||||
else:
|
||||
number = Inf*sign
|
||||
return i - start
|
||||
|
||||
# if integer is representable in 53 bits: fast path
|
||||
# max fast path integer is 1<<53 - 1 or 8999999999999999 (16 digits)
|
||||
let digits = kdigits + fdigits
|
||||
if digits <= 15 or (digits <= 16 and first_digit <= 8):
|
||||
# max float power of ten with set bits above the 53th bit is 10^22
|
||||
if abs_exponent <= 22:
|
||||
if exp_negative:
|
||||
number = sign * integer.float / powtens[abs_exponent]
|
||||
else:
|
||||
number = sign * integer.float * powtens[abs_exponent]
|
||||
return i - start
|
||||
|
||||
# if exponent is greater try to fit extra exponent above 22 by multiplying
|
||||
# integer part is there is space left.
|
||||
let slop = 15 - kdigits - fdigits
|
||||
if abs_exponent <= 22 + slop and not exp_negative:
|
||||
number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop]
|
||||
return i - start
|
||||
|
||||
# if failed: slow path with strtod.
|
||||
var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
|
||||
var ti = 0
|
||||
let maxlen = t.high - "e+000".len # reserve enough space for exponent
|
||||
|
||||
result = i - start
|
||||
i = start
|
||||
# re-parse without error checking, any error should be handled by the code above.
|
||||
if s[i] == '.': i.inc
|
||||
while s[i] in {'0'..'9','+','-'}:
|
||||
if ti < maxlen:
|
||||
t[ti] = s[i]; inc(ti)
|
||||
inc(i)
|
||||
while s[i] in {'.', '_'}: # skip underscore and decimal point
|
||||
inc(i)
|
||||
|
||||
# insert exponent
|
||||
t[ti] = 'E'; inc(ti)
|
||||
t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
|
||||
inc(ti, 3)
|
||||
|
||||
# insert adjusted exponent
|
||||
t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
|
||||
t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
|
||||
t[ti-3] = ('0'.ord + abs_exponent mod 10).char
|
||||
|
||||
when defined(nimNoArrayToCstringConversion):
|
||||
number = c_strtod(addr t, nil)
|
||||
else:
|
||||
number = c_strtod(t, nil)
|
||||
|
||||
proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
|
||||
result = newStringOfCap(sizeof(x)*4)
|
||||
result.add x
|
||||
|
||||
proc nimBoolToStr(x: bool): string {.compilerRtl.} =
|
||||
return if x: "true" else: "false"
|
||||
|
||||
proc nimCharToStr(x: char): string {.compilerRtl.} =
|
||||
result = newString(1)
|
||||
result[0] = x
|
||||
|
||||
Reference in New Issue
Block a user