Merge branch 'devel' into araq
This commit is contained in:
commit
e9243a1616
152 changed files with 3012 additions and 3097 deletions
|
|
@ -75,7 +75,8 @@ type
|
|||
nnkClosure,
|
||||
nnkGotoState,
|
||||
nnkState,
|
||||
nnkBreakState
|
||||
nnkBreakState,
|
||||
nnkFuncDef
|
||||
|
||||
NimNodeKinds* = set[NimNodeKind]
|
||||
NimTypeKind* = enum # some types are no longer used, see ast.nim
|
||||
|
|
@ -96,14 +97,14 @@ type
|
|||
ntyError,
|
||||
ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst,
|
||||
ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot,
|
||||
ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid
|
||||
ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid
|
||||
|
||||
TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
|
||||
NimSymKind* = enum
|
||||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
||||
nskConst, nskResult,
|
||||
nskProc, nskMethod, nskIterator,
|
||||
nskProc, nskFunc, nskMethod, nskIterator,
|
||||
nskConverter, nskMacro, nskTemplate, nskField,
|
||||
nskEnumField, nskForVar, nskLabel,
|
||||
nskStub
|
||||
|
|
@ -843,7 +844,8 @@ proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
|
|||
|
||||
|
||||
const
|
||||
RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
|
||||
RoutineNodes* = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
|
||||
nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
|
||||
AtomicNodes* = {nnkNone..nnkNilLit}
|
||||
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||
nnkCallStrLit, nnkHiddenCallConv}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ type
|
|||
akUInt16 = 42, ## any represents an unsigned in16
|
||||
akUInt32 = 43, ## any represents an unsigned int32
|
||||
akUInt64 = 44, ## any represents an unsigned int64
|
||||
# akOpt = 44+18 ## the builtin 'opt' type.
|
||||
|
||||
Any* = object ## can represent any nim value; NOTE: the wrapped
|
||||
## value can be modified with its wrapper! This means
|
||||
|
|
|
|||
|
|
@ -31,8 +31,12 @@ struct Nim::SysThread
|
|||
void entry() override {
|
||||
(_func)(_arg); }
|
||||
|
||||
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg)
|
||||
: Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg)
|
||||
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg, int affinity)
|
||||
: Genode::Thread(env, "nim-thread", stack_size,
|
||||
env.cpu().affinity_space().location_of_index(affinity),
|
||||
Genode::Cpu_session::Weight(Genode::Cpu_session::Weight::DEFAULT_WEIGHT-1),
|
||||
env.cpu()),
|
||||
_func(func), _arg(arg)
|
||||
{
|
||||
Genode::Thread::start();
|
||||
}
|
||||
|
|
@ -40,8 +44,8 @@ struct Nim::SysThread
|
|||
|
||||
Genode::Constructible<Thread> _thread;
|
||||
|
||||
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) {
|
||||
_thread.construct(*env, stack_size, func, arg); }
|
||||
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg, int aff) {
|
||||
_thread.construct(*env, stack_size, func, arg, aff); }
|
||||
|
||||
void joinThread() {
|
||||
_thread->join(); }
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
|
|||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.Pstmt
|
||||
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
|
||||
if step(stmt) == SQLITE_DONE:
|
||||
let x = step(stmt)
|
||||
if x in {SQLITE_DONE, SQLITE_ROW}:
|
||||
result = finalize(stmt) == SQLITE_OK
|
||||
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
|
|||
result = quote do:
|
||||
(`dotOp`(`obj`, `field`))()
|
||||
for elem in args:
|
||||
result[0].add elem
|
||||
result.add elem
|
||||
|
||||
# Iterators:
|
||||
|
||||
|
|
@ -471,7 +471,7 @@ macro bindMethod*(procedure: typed): auto =
|
|||
# construct the `this` parameter:
|
||||
thisQuote = quote do:
|
||||
var `this` {. nodecl, importc .} : `thisType`
|
||||
call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0][0])
|
||||
call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0])
|
||||
# construct the procedure call inside the method
|
||||
if args.len > 2:
|
||||
for idx in 2..args.len-1:
|
||||
|
|
@ -483,6 +483,6 @@ macro bindMethod*(procedure: typed): auto =
|
|||
params,
|
||||
rawProc[4],
|
||||
rawProc[5],
|
||||
newTree(nnkStmtList, thisQuote[0], call)
|
||||
newTree(nnkStmtList, thisQuote, call)
|
||||
)
|
||||
result = body
|
||||
|
|
|
|||
|
|
@ -373,11 +373,13 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
|
|||
|
||||
#define float64ToInt64(x) ((NI64) (x))
|
||||
|
||||
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
|
||||
|
||||
#define STRING_LITERAL(name, str, length) \
|
||||
static const struct { \
|
||||
TGenericSeq Sup; \
|
||||
NIM_CHAR data[(length) + 1]; \
|
||||
} name = {{length, length}, str}
|
||||
static const struct { \
|
||||
TGenericSeq Sup; \
|
||||
NIM_CHAR data[(length) + 1]; \
|
||||
} name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
|
||||
|
||||
typedef struct TStringDesc* string;
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ const
|
|||
"interface", "is", "isnot", "iterator", "let", "macro", "method",
|
||||
"mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
|
||||
"ptr", "raise", "ref", "return", "shl", "shr", "static",
|
||||
"template", "try", "tuple", "type", "using", "var", "when", "while", "with",
|
||||
"without", "xor", "yield"]
|
||||
"template", "try", "tuple", "type", "using", "var", "when", "while",
|
||||
"xor", "yield"]
|
||||
|
||||
proc getSourceLanguage*(name: string): SourceLanguage =
|
||||
for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
|
||||
|
|
|
|||
|
|
@ -1,855 +0,0 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import math
|
||||
import strutils
|
||||
|
||||
|
||||
## Basic 2d support with vectors, points, matrices and some basic utilities.
|
||||
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
||||
## the translation part of matrix is ignored.
|
||||
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars.
|
||||
##
|
||||
## Quick start example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
##
|
||||
## # Create a 2d point at (100,0) and a vector (5,2)
|
||||
##
|
||||
## var pt:Point2d=point2d(100.0,0.0)
|
||||
##
|
||||
## var vec:Vector2d=vector2d(5.0,2.0)
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
const
|
||||
DEG360* = PI * 2.0
|
||||
## 360 degrees in radians.
|
||||
DEG270* = PI * 1.5
|
||||
## 270 degrees in radians.
|
||||
DEG180* = PI
|
||||
## 180 degrees in radians.
|
||||
DEG90* = PI / 2.0
|
||||
## 90 degrees in radians.
|
||||
DEG60* = PI / 3.0
|
||||
## 60 degrees in radians.
|
||||
DEG45* = PI / 4.0
|
||||
## 45 degrees in radians.
|
||||
DEG30* = PI / 6.0
|
||||
## 30 degrees in radians.
|
||||
DEG15* = PI / 12.0
|
||||
## 15 degrees in radians.
|
||||
RAD2DEGCONST = 180.0 / PI
|
||||
## used internally by DegToRad and RadToDeg
|
||||
|
||||
type
|
||||
Matrix2d* = object
|
||||
## Implements a row major 2d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
## The rightmost column of the 3x3 matrix is left out since normally
|
||||
## not used for geometric transformations in 2d.
|
||||
ax*,ay*,bx*,by*,tx*,ty*:float
|
||||
Point2d* = object
|
||||
## Implements a non-homogeneous 2d point stored as
|
||||
## an `x` coordinate and an `y` coordinate.
|
||||
x*,y*:float
|
||||
Vector2d* = object
|
||||
## Implements a 2d **direction vector** stored as
|
||||
## an `x` coordinate and an `y` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*:float
|
||||
{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].}
|
||||
|
||||
|
||||
# Some forward declarations...
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
|
||||
## Creates a new matrix.
|
||||
## `ax`,`ay` is the local x axis
|
||||
## `bx`,`by` is the local y axis
|
||||
## `tx`,`ty` is the translation
|
||||
proc vector2d*(x,y:float):Vector2d {.noInit,inline.}
|
||||
## Returns a new vector (`x`,`y`)
|
||||
proc point2d*(x,y:float):Point2d {.noInit,inline.}
|
||||
## Returns a new point (`x`,`y`)
|
||||
|
||||
|
||||
|
||||
let
|
||||
IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
|
||||
## Quick access to an identity matrix
|
||||
ORIGO*:Point2d=point2d(0.0,0.0)
|
||||
## Quick access to point (0,0)
|
||||
XAXIS*:Vector2d=vector2d(1.0,0.0)
|
||||
## Quick access to an 2d x-axis unit vector
|
||||
YAXIS*:Vector2d=vector2d(0.0,1.0)
|
||||
## Quick access to an 2d y-axis unit vector
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Private utils
|
||||
# ***************************************
|
||||
|
||||
proc rtos(val:float):string=
|
||||
return formatFloat(val,ffDefault,0)
|
||||
|
||||
proc safeArccos(v:float):float=
|
||||
## assumes v is in range 0.0-1.0, but clamps
|
||||
## the value to avoid out of domain errors
|
||||
## due to rounding issues
|
||||
return arccos(clamp(v,-1.0,1.0))
|
||||
|
||||
|
||||
template makeBinOpVector(s) =
|
||||
## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors
|
||||
proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
|
||||
template makeBinOpAssignVector(s)=
|
||||
## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors
|
||||
proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Matrix2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
## Sets arbitrary elements in an existing matrix.
|
||||
t.ax=ax
|
||||
t.ay=ay
|
||||
t.bx=bx
|
||||
t.by=by
|
||||
t.tx=tx
|
||||
t.ty=ty
|
||||
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
|
||||
result.setElements(ax,ay,bx,by,tx,ty)
|
||||
|
||||
proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
|
||||
## Concatenates matrices returning a new matrix.
|
||||
|
||||
# | a.AX a.AY 0 | | b.AX b.AY 0 |
|
||||
# | a.BX a.BY 0 | * | b.BX b.BY 0 |
|
||||
# | a.TX a.TY 1 | | b.TX b.TY 1 |
|
||||
result.setElements(
|
||||
a.ax * b.ax + a.ay * b.bx,
|
||||
a.ax * b.ay + a.ay * b.by,
|
||||
a.bx * b.ax + a.by * b.bx,
|
||||
a.bx * b.ay + a.by * b.by,
|
||||
a.tx * b.ax + a.ty * b.bx + b.tx,
|
||||
a.tx * b.ay + a.ty * b.by + b.ty)
|
||||
|
||||
|
||||
proc scale*(s:float):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix.
|
||||
result.setElements(s,0,0,s,0,0)
|
||||
|
||||
proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y)
|
||||
|
||||
proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
result.setElements(sx,0,0,sy,0,0)
|
||||
|
||||
proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
|
||||
|
||||
proc move*(dx,dy:float):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,1,dx,dy)
|
||||
|
||||
proc move*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix from a vector.
|
||||
result.setElements(1,0,0,1,v.x,v.y)
|
||||
|
||||
proc rotate*(rad:float):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,0,0)
|
||||
|
||||
proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians around
|
||||
## the origin `org`
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
|
||||
|
||||
proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through origo and
|
||||
## has the direction of `v`
|
||||
let
|
||||
sqx=v.x*v.x
|
||||
sqy=v.y*v.y
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=v.x*v.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
|
||||
|
||||
result.setElements(
|
||||
sqd,xy2,
|
||||
xy2,-sqd,
|
||||
0.0,0.0)
|
||||
|
||||
proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through `org` and
|
||||
## has the direction of `v`
|
||||
let
|
||||
sqx=v.x*v.x
|
||||
sqy=v.y*v.y
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=v.x*v.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
|
||||
|
||||
result.setElements(
|
||||
sqd,xy2,
|
||||
xy2,-sqd,
|
||||
org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd)
|
||||
|
||||
|
||||
|
||||
proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
|
||||
## Returns a new skew matrix, which has its
|
||||
## x axis rotated `xskew` radians from the local x axis, and
|
||||
## y axis rotated `yskew` radians from the local y axis
|
||||
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
|
||||
|
||||
|
||||
proc `$`* (t:Matrix2d):string {.noInit.} =
|
||||
## Returns a string representation of the matrix
|
||||
return rtos(t.ax) & "," & rtos(t.ay) &
|
||||
"," & rtos(t.bx) & "," & rtos(t.by) &
|
||||
"," & rtos(t.tx) & "," & rtos(t.ty)
|
||||
|
||||
proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a circle into an ellipse.
|
||||
## `tol` is used as tolerance for both equal length comparison
|
||||
## and perp. comparison.
|
||||
|
||||
#dot product=0 means perpendicular coord. system:
|
||||
if abs(t.ax*t.bx+t.ay*t.by)<=tol:
|
||||
#subtract squared lengths of axes to check if uniform scaling:
|
||||
if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc determinant*(t:Matrix2d):float=
|
||||
## Computes the determinant of the matrix.
|
||||
|
||||
#NOTE: equivalent with perp.dot product for two 2d vectors
|
||||
return t.ax*t.by-t.bx*t.ay
|
||||
|
||||
proc isMirroring* (m:Matrix2d):bool=
|
||||
## Checks if the `m` is a mirroring matrix,
|
||||
## which means it will reverse direction of a curve transformed with it
|
||||
return m.determinant<0.0
|
||||
|
||||
proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
## Returns a new matrix, which is the inverse of the matrix
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
let d=m.determinant
|
||||
if d==0.0:
|
||||
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
|
||||
|
||||
result.setElements(
|
||||
m.by/d,-m.ay/d,
|
||||
-m.bx/d,m.ax/d,
|
||||
(m.bx*m.ty-m.by*m.tx)/d,
|
||||
(m.ay*m.tx-m.ax*m.ty)/d)
|
||||
|
||||
proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
abs(m1.ax-m2.ax)<=tol and
|
||||
abs(m1.ay-m2.ay)<=tol and
|
||||
abs(m1.bx-m2.bx)<=tol and
|
||||
abs(m1.by-m2.by)<=tol and
|
||||
abs(m1.tx-m2.tx)<=tol and
|
||||
abs(m1.ty-m2.ty)<=tol
|
||||
|
||||
proc `=~`*(m1,m2:Matrix2d):bool=
|
||||
## Checks if `m1`and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks is a matrix is approximately an identity matrix,
|
||||
## using `tol` as tolerance for each element.
|
||||
return equals(m,IDMATRIX,tol)
|
||||
|
||||
proc apply*(m:Matrix2d,x,y:var float,translate=false)=
|
||||
## Applies transformation `m` onto `x`,`y`, optionally
|
||||
## using the translation part of the matrix.
|
||||
if translate: # positional style transform
|
||||
let newx=x*m.ax+y*m.bx+m.tx
|
||||
y=x*m.ay+y*m.by+m.ty
|
||||
x=newx
|
||||
else: # delta style transform
|
||||
let newx=x*m.ax+y*m.bx
|
||||
y=x*m.ay+y*m.by
|
||||
x=newx
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Vector2d implementation
|
||||
# ***************************************
|
||||
proc vector2d*(x,y:float):Vector2d = #forward decl.
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector with angle `ang` and magnitude `len`
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector having slope (dy/dx) given by
|
||||
## `slope`, and a magnitude of `len`
|
||||
let ang=arctan(slope)
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc len*(v:Vector2d):float {.inline.}=
|
||||
## Returns the length of the vector.
|
||||
sqrt(v.x*v.x+v.y*v.y)
|
||||
|
||||
proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its angle.
|
||||
let fac=newlen/v.len
|
||||
|
||||
if newlen==0.0:
|
||||
v.x=0.0
|
||||
v.y=0.0
|
||||
return
|
||||
|
||||
if fac==Inf or fac==NegInf:
|
||||
#to short for float accuracy
|
||||
#do as good as possible:
|
||||
v.x=newlen
|
||||
v.y=0.0
|
||||
else:
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc sqrLen*(v:Vector2d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
v.x*v.x+v.y*v.y
|
||||
|
||||
proc angle*(v:Vector2d):float=
|
||||
## Returns the angle of the vector.
|
||||
## (The counter clockwise plane angle between posetive x axis and `v`)
|
||||
result=arctan2(v.y,v.x)
|
||||
if result<0.0: result+=DEG360
|
||||
|
||||
proc `$` *(v:Vector2d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
result.add(rtos(v.y))
|
||||
|
||||
|
||||
proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | 0 0 1 |
|
||||
result.x=v.x*m.ax+v.y*m.bx
|
||||
result.y=v.x*m.ay+v.y*m.by
|
||||
|
||||
|
||||
proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | 0 0 1 |
|
||||
let newx=v.x*m.ax+v.y*m.bx
|
||||
v.y=v.x*m.ay+v.y*m.by
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc tryNormalize*(v:var Vector2d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and
|
||||
## false is returned, otherwise true is returned.
|
||||
|
||||
let mag=v.len
|
||||
|
||||
if mag==0.0:
|
||||
return false
|
||||
|
||||
v.x/=mag
|
||||
v.y/=mag
|
||||
return true
|
||||
|
||||
|
||||
proc normalize*(v:var Vector2d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc transformNorm*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies a normal direction transformation `t` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## (determinant=0), an EDivByZero will be raised.
|
||||
|
||||
# transforming a normal is done by transforming
|
||||
# by the transpose of the inverse of the original matrix
|
||||
# this can be heavily optimized by precompute and inline
|
||||
# | | AX AY 0 | ^-1| ^T
|
||||
# | X Y 1 | * | | BX BY 0 | |
|
||||
# | | 0 0 1 | |
|
||||
let d=t.determinant
|
||||
if(d==0.0):
|
||||
raise newException(DivByZeroError,"Matrix is not invertible")
|
||||
let newx = (t.by*v.x-t.ay*v.y)/d
|
||||
v.y = (t.ax*v.y-t.bx*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies inverse of a transformation `t` to `v` in place.
|
||||
## This is faster than creating an inverse matrix and apply() it.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix. If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
let d=t.determinant
|
||||
|
||||
if(d==0.0):
|
||||
raise newException(DivByZeroError,"Matrix is not invertible")
|
||||
|
||||
let newx=(t.by*v.x-t.bx*v.y)/d
|
||||
v.y = (t.ax*v.y-t.ay*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies an inverse normal direction transformation `t` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
|
||||
# normal inverse transform is done by transforming
|
||||
# by the inverse of the transpose of the inverse of the org. matrix
|
||||
# which is equivalent with transforming with the transpose.
|
||||
# | | | AX AY 0 |^-1|^T|^-1 | AX BX 0 |
|
||||
# | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 |
|
||||
# | | | 0 0 1 | | | | 0 0 1 |
|
||||
# This can be heavily reduced to:
|
||||
let newx=t.ay*v.y+t.ax*v.x
|
||||
v.y=t.by*v.y+t.bx*v.x
|
||||
v.x=newx
|
||||
|
||||
proc rotate90*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 90 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.x= -v.x
|
||||
|
||||
proc rotate180*(v:var Vector2d){.inline.}=
|
||||
## Quickly rotates vector `v` 180 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
v.x= -v.x
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate270*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 270 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate*(v:var Vector2d,rad:float) =
|
||||
## Rotates vector `v` `rad` radians in place.
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
newx=c*v.x-s*v.y
|
||||
v.y=c*v.y+s*v.x
|
||||
v.x=newx
|
||||
|
||||
proc scale*(v:var Vector2d,fac:float){.inline.}=
|
||||
## Scales vector `v` `rad` radians in place.
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
|
||||
## Stretches vector `v` `facx` times horizontally,
|
||||
## and `facy` times vertically.
|
||||
v.x*=facx
|
||||
v.y*=facy
|
||||
|
||||
proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
|
||||
## Mirrors vector `v` using `mirrvec` as mirror direction.
|
||||
let
|
||||
sqx=mirrvec.x*mirrvec.x
|
||||
sqy=mirrvec.y*mirrvec.y
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=mirrvec.x*mirrvec.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return #mirroring around a zero vector is arbitrary=>keep as is is fastest
|
||||
|
||||
let newx=xy2*v.y+sqd*v.x
|
||||
v.y=v.x*xy2-sqd*v.y
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc `-` *(v:Vector2d):Vector2d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
|
||||
# declare templated binary operators
|
||||
makeBinOpVector(`+`)
|
||||
makeBinOpVector(`-`)
|
||||
makeBinOpVector(`*`)
|
||||
makeBinOpVector(`/`)
|
||||
makeBinOpAssignVector(`+=`)
|
||||
makeBinOpAssignVector(`-=`)
|
||||
makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
|
||||
proc dot*(v1,v2:Vector2d):float=
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y
|
||||
|
||||
proc cross*(v1,v2:Vector2d):float=
|
||||
## Computes the cross product of two vectors, also called
|
||||
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
|
||||
## are parallel.
|
||||
return v1.x*v2.y-v1.y*v2.x
|
||||
|
||||
proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
|
||||
|
||||
proc `=~` *(v1,v2:Vector2d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
proc angleTo*(v1,v2:Vector2d):float=
|
||||
## Returns the smallest of the two possible angles
|
||||
## between `v1` and `v2` in radians.
|
||||
var
|
||||
nv1=v1
|
||||
nv2=v2
|
||||
if not nv1.tryNormalize or not nv2.tryNormalize:
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
proc angleCCW*(v1,v2:Vector2d):float=
|
||||
## Returns the counter clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
if v1.cross(v2)>=0.0:
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc angleCW*(v1,v2:Vector2d):float=
|
||||
## Returns the clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
if v1.cross(v2)<=0.0:
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc turnAngle*(v1,v2:Vector2d):float=
|
||||
## Returns the amount v1 should be rotated (in radians) to equal v2,
|
||||
## in range -PI to PI
|
||||
let a=v1.angleTo(v2)
|
||||
if v1.cross(v2)<=0.0:
|
||||
return -a
|
||||
return a
|
||||
|
||||
proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
## an arbitrary normalized vector is returned.
|
||||
var
|
||||
vmag1=v1.len
|
||||
vmag2=v2.len
|
||||
|
||||
# zero length vector equals arbitrary vector, just change to magnitude to one to
|
||||
# avoid zero division
|
||||
if vmag1==0.0:
|
||||
if vmag2==0: #both are zero length return any normalized vector
|
||||
return XAXIS
|
||||
vmag1=1.0
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
let
|
||||
x1=v1.x/vmag1
|
||||
y1=v1.y/vmag1
|
||||
x2=v2.x/vmag2
|
||||
y2=v2.y/vmag2
|
||||
|
||||
result.x=(x1 + x2) * 0.5
|
||||
result.y=(y1 + y2) * 0.5
|
||||
|
||||
if not result.tryNormalize():
|
||||
# This can happen if vectors are colinear. In this special case
|
||||
# there are actually two bisectors, we select just
|
||||
# one of them (x1,y1 rotated 90 degrees ccw).
|
||||
result.x = -y1
|
||||
result.y = x1
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Point2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc point2d*(x,y:float):Point2d =
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc sqrDist*(a,b:Point2d):float=
|
||||
## Computes the squared distance between `a` and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=dx*dx+dy*dy
|
||||
|
||||
proc dist*(a,b:Point2d):float {.inline.}=
|
||||
## Computes the absolute distance between `a` and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
||||
proc angle*(a,b:Point2d):float=
|
||||
## Computes the angle of the vector `b`-`a`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=arctan2(dy,dx)
|
||||
if result<0:
|
||||
result += DEG360
|
||||
|
||||
proc `$` *(p:Point2d):string=
|
||||
## String representation of `p`
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
result.add(rtos(p.y))
|
||||
|
||||
proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
|
||||
## Concatenates a point `p` with a transform `t`,
|
||||
## resulting in a new, transformed point.
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | TX TY 1 |
|
||||
result.x=p.x*t.ax+p.y*t.bx+t.tx
|
||||
result.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
|
||||
proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}=
|
||||
## Applies transformation `t` onto `p` in place.
|
||||
let newx=p.x*t.ax+p.y*t.bx+t.tx
|
||||
p.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
|
||||
## Applies the inverse of transformation `t` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
|
||||
# | AX AY 0 | ^-1
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | TX TY 1 |
|
||||
let d=t.determinant
|
||||
if d==0.0:
|
||||
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
|
||||
let
|
||||
newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d
|
||||
p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
|
||||
proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
|
||||
proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} =
|
||||
## Subtracts `p2`from `p1` resulting in a difference vector.
|
||||
result.x=p1.x-p2.x
|
||||
result.y=p1.y-p2.y
|
||||
|
||||
proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
|
||||
proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:Point2d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} =
|
||||
## Returns a point with a given angle and distance away from `p`
|
||||
result.x=p.x+cos(ang)*dist
|
||||
result.y=p.y+sin(ang)*dist
|
||||
|
||||
proc rotate*(p:var Point2d,rad:float)=
|
||||
## Rotates a point in place `rad` radians around origo.
|
||||
let
|
||||
c=cos(rad)
|
||||
s=sin(rad)
|
||||
newx=p.x*c-p.y*s
|
||||
p.y=p.y*c+p.x*s
|
||||
p.x=newx
|
||||
|
||||
proc rotate*(p:var Point2d,rad:float,org:Point2d)=
|
||||
## Rotates a point in place `rad` radians using `org` as
|
||||
## center of rotation.
|
||||
let
|
||||
c=cos(rad)
|
||||
s=sin(rad)
|
||||
newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x
|
||||
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
|
||||
p.x=newx
|
||||
|
||||
proc scale*(p:var Point2d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
|
||||
proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
|
||||
proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` and `facy` times with
|
||||
## world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
|
||||
proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` and `facy` times with
|
||||
## `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
p.y=(p.y - org.y) * facy + org.y
|
||||
|
||||
proc move*(p:var Point2d,dx,dy:float){.inline.}=
|
||||
## Translates a point `dx`, `dy` in place.
|
||||
p.x+=dx
|
||||
p.y+=dy
|
||||
|
||||
proc move*(p:var Point2d,v:Vector2d){.inline.}=
|
||||
## Translates a point with vector `v` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc sgnArea*(a,b,c:Point2d):float=
|
||||
## Computes the signed area of the triangle thru points `a`,`b` and `c`
|
||||
## result>0.0 for counter clockwise triangle
|
||||
## result<0.0 for clockwise triangle
|
||||
## This is commonly used to determinate side of a point with respect to a line.
|
||||
return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5
|
||||
|
||||
proc area*(a,b,c:Point2d):float=
|
||||
## Computes the area of the triangle thru points `a`,`b` and `c`
|
||||
return abs(sgnArea(a,b,c))
|
||||
|
||||
proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
|
||||
## Returns a point selected from `pts`, that has the closest
|
||||
## euclidean distance to `p`
|
||||
assert(pts.len>0) # must have at least one point
|
||||
|
||||
var
|
||||
bestidx=0
|
||||
bestdist=p.sqrDist(pts[0])
|
||||
curdist:float
|
||||
|
||||
for idx in 1..high(pts):
|
||||
curdist=p.sqrDist(pts[idx])
|
||||
if curdist<bestdist:
|
||||
bestidx=idx
|
||||
bestdist=curdist
|
||||
|
||||
result=pts[bestidx]
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Misc. math utilities that should
|
||||
# probably be in another module.
|
||||
# ***************************************
|
||||
proc normAngle*(ang:float):float=
|
||||
## Returns an angle in radians, that is equal to `ang`,
|
||||
## but in the range 0 to <2*PI
|
||||
if ang>=0.0 and ang<DEG360:
|
||||
return ang
|
||||
|
||||
return ang mod DEG360
|
||||
|
||||
proc degToRad*(deg:float):float {.inline.}=
|
||||
## converts `deg` degrees to radians
|
||||
deg / RAD2DEGCONST
|
||||
|
||||
proc radToDeg*(rad:float):float {.inline.}=
|
||||
## converts `rad` radians to degrees
|
||||
rad * RAD2DEGCONST
|
||||
1040
lib/pure/basic3d.nim
1040
lib/pure/basic3d.nim
File diff suppressed because it is too large
Load diff
|
|
@ -287,8 +287,6 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
|
|||
|
||||
if i >= 0:
|
||||
result = false
|
||||
s.data[i].hcode = 0
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
dec(s.counter)
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
|
|
@ -300,7 +298,7 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
|
|||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||
return
|
||||
r = s.data[i].hcode and msk # "home" location of key@i
|
||||
shallowCopy(s.data[j], s.data[i]) # data[j] will be marked EMPTY next loop
|
||||
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
||||
|
||||
proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
|
||||
## Excludes `key` in the set `s` and tells if `key` was removed from `s`.
|
||||
|
|
@ -662,9 +660,12 @@ proc card*[A](s: OrderedSet[A]): int {.inline.} =
|
|||
|
||||
template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
|
||||
var h = s.first
|
||||
var idx = 0
|
||||
while h >= 0:
|
||||
var nxt = s.data[h].next
|
||||
if isFilled(s.data[h].hcode): yieldStmt
|
||||
if isFilled(s.data[h].hcode):
|
||||
yieldStmt
|
||||
inc(idx)
|
||||
h = nxt
|
||||
|
||||
iterator items*[A](s: OrderedSet[A]): A =
|
||||
|
|
@ -689,6 +690,11 @@ iterator items*[A](s: OrderedSet[A]): A =
|
|||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
||||
assert s.isValid, "The set needs to be initialized"
|
||||
forAllOrderedPairs:
|
||||
yield (idx, s.data[h].key)
|
||||
|
||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
|
|
@ -760,6 +766,67 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
|
|||
assert other.isValid, "The set `other` needs to be initialized."
|
||||
for item in other: incl(s, item)
|
||||
|
||||
proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: Hash
|
||||
var i = rawGet(s, key, hc)
|
||||
var msk = high(s.data)
|
||||
result = true
|
||||
|
||||
if i >= 0:
|
||||
result = false
|
||||
# Fix ordering
|
||||
if s.first == i:
|
||||
s.first = s.data[i].next
|
||||
else:
|
||||
var itr = s.first
|
||||
while true:
|
||||
if (s.data[itr].next == i):
|
||||
s.data[itr].next = s.data[i].next
|
||||
if s.last == i:
|
||||
s.last = itr
|
||||
break
|
||||
itr = s.data[itr].next
|
||||
|
||||
dec(s.counter)
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
var r = j # though may be adaptable to other simple sequences.
|
||||
s.data[i].hcode = 0 # mark current EMPTY
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
s.data[i].next = 0
|
||||
doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||
i = (i + 1) and msk # increment mod table size
|
||||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||
return
|
||||
r = s.data[i].hcode and msk # "home" location of key@i
|
||||
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
||||
|
||||
proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
|
||||
## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n).
|
||||
##
|
||||
## The difference with regards to the `excl() <#excl,TOrderedSet[A],A>`_ proc is
|
||||
## that this proc returns `true` if `key` was not present in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var s = toOrderedSet([2, 3, 6, 7])
|
||||
## assert s.missingOrExcl(4) == true
|
||||
## assert s.missingOrExcl(6) == false
|
||||
exclImpl(s, key)
|
||||
|
||||
|
||||
proc excl*[A](s: var OrderedSet[A], key: A) =
|
||||
## Excludes `key` from the set `s`. Efficiency: O(n).
|
||||
##
|
||||
## This doesn't do anything if `key` is not found in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var s = toOrderedSet([2, 3, 6, 7])
|
||||
## s.excl(2)
|
||||
## s.excl(2)
|
||||
## assert s.len == 3
|
||||
discard exclImpl(s, key)
|
||||
|
||||
proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
|
||||
## Includes `key` in the set `s` and tells if `key` was added to `s`.
|
||||
##
|
||||
|
|
@ -986,6 +1053,24 @@ when isMainModule and not defined(release):
|
|||
assert a.len == b.card
|
||||
assert a.len == 2
|
||||
|
||||
block setPairsIterator:
|
||||
var s = toOrderedSet([1, 3, 5, 7])
|
||||
var items = newSeq[tuple[a: int, b: int]]()
|
||||
for idx, item in s: items.add((idx, item))
|
||||
assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)]
|
||||
|
||||
block exclusions:
|
||||
var s = toOrderedSet([1, 2, 3, 6, 7, 4])
|
||||
|
||||
s.excl(3)
|
||||
s.excl(3)
|
||||
s.excl(1)
|
||||
s.excl(4)
|
||||
|
||||
var items = newSeq[int]()
|
||||
for item in s: items.add item
|
||||
assert items == @[2, 6, 7]
|
||||
|
||||
#block orderedSetIterator:
|
||||
# var a = initOrderedSet[int]()
|
||||
# for value in [9, 2, 1, 5, 1, 8, 4, 2]:
|
||||
|
|
@ -1030,6 +1115,11 @@ when isMainModule and not defined(release):
|
|||
if s <= i or mustRehash(s, i):
|
||||
echo "performance issue: rightSize() will not elide enlarge() at ", i
|
||||
|
||||
block missingOrExcl:
|
||||
var s = toOrderedSet([2, 3, 6, 7])
|
||||
assert s.missingOrExcl(4) == true
|
||||
assert s.missingOrExcl(6) == false
|
||||
|
||||
when not defined(testing):
|
||||
echo "Micro tests run successfully."
|
||||
|
||||
|
|
|
|||
|
|
@ -721,6 +721,16 @@ proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] =
|
|||
if n.isNil or n.kind != JArray: return default
|
||||
else: return n.elems
|
||||
|
||||
proc add*(father, child: JsonNode) =
|
||||
## Adds `child` to a JArray node `father`.
|
||||
assert father.kind == JArray
|
||||
father.elems.add(child)
|
||||
|
||||
proc add*(obj: JsonNode, key: string, val: JsonNode) =
|
||||
## Sets a field from a `JObject`.
|
||||
assert obj.kind == JObject
|
||||
obj.fields[key] = val
|
||||
|
||||
proc `%`*(s: string): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JString JsonNode`.
|
||||
new(result)
|
||||
|
|
@ -759,6 +769,19 @@ proc `%`*[T](elements: openArray[T]): JsonNode =
|
|||
result = newJArray()
|
||||
for elem in elements: result.add(%elem)
|
||||
|
||||
when false:
|
||||
# For 'consistency' we could do this, but that only pushes people further
|
||||
# into that evil comfort zone where they can use Nim without understanding it
|
||||
# causing problems later on.
|
||||
proc `%`*(elements: set[bool]): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JObject JsonNode`.
|
||||
## This can only be used with the empty set ``{}`` and is supported
|
||||
## to prevent the gotcha ``%*{}`` which used to produce an empty
|
||||
## JSON array.
|
||||
result = newJObject()
|
||||
assert false notin elements, "usage error: only empty sets allowed"
|
||||
assert true notin elements, "usage error: only empty sets allowed"
|
||||
|
||||
proc `%`*(o: object): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JObject JsonNode`
|
||||
result = newJObject()
|
||||
|
|
@ -779,27 +802,25 @@ proc `%`*(o: enum): JsonNode =
|
|||
proc toJson(x: NimNode): NimNode {.compiletime.} =
|
||||
case x.kind
|
||||
of nnkBracket: # array
|
||||
if x.len == 0: return newCall(bindSym"newJArray")
|
||||
result = newNimNode(nnkBracket)
|
||||
for i in 0 .. <x.len:
|
||||
result.add(toJson(x[i]))
|
||||
|
||||
result = newCall(bindSym"%", result)
|
||||
of nnkTableConstr: # object
|
||||
if x.len == 0: return newCall(bindSym"newJObject")
|
||||
result = newNimNode(nnkTableConstr)
|
||||
for i in 0 .. <x.len:
|
||||
x[i].expectKind nnkExprColonExpr
|
||||
result.add(newNimNode(nnkExprColonExpr).add(x[i][0]).add(toJson(x[i][1])))
|
||||
|
||||
result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
|
||||
result = newCall(bindSym"%", result)
|
||||
of nnkCurly: # empty object
|
||||
result = newNimNode(nnkTableConstr)
|
||||
x.expectLen(0)
|
||||
|
||||
result = newCall(bindSym"newJObject")
|
||||
of nnkNilLit:
|
||||
result = newCall("newJNull")
|
||||
|
||||
result = newCall(bindSym"newJNull")
|
||||
else:
|
||||
result = x
|
||||
|
||||
result = prefix(result, "%")
|
||||
result = newCall(bindSym"%", x)
|
||||
|
||||
macro `%*`*(x: untyped): untyped =
|
||||
## Convert an expression to a JsonNode directly, without having to specify
|
||||
|
|
@ -909,16 +930,6 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
|
|||
proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
|
||||
## Deprecated for `hasKey`
|
||||
|
||||
proc add*(father, child: JsonNode) =
|
||||
## Adds `child` to a JArray node `father`.
|
||||
assert father.kind == JArray
|
||||
father.elems.add(child)
|
||||
|
||||
proc add*(obj: JsonNode, key: string, val: JsonNode) =
|
||||
## Sets a field from a `JObject`.
|
||||
assert obj.kind == JObject
|
||||
obj.fields[key] = val
|
||||
|
||||
proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
|
||||
## Sets a field from a `JObject`.
|
||||
assert(obj.kind == JObject)
|
||||
|
|
@ -1203,7 +1214,7 @@ proc parseJson(p: var JsonParser): JsonNode =
|
|||
raiseParseErr(p, "{")
|
||||
|
||||
when not defined(js):
|
||||
proc parseJson*(s: Stream, filename: string): JsonNode =
|
||||
proc parseJson*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||
|
|
@ -1934,4 +1945,8 @@ when isMainModule:
|
|||
except JsonParsingError:
|
||||
doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
|
||||
|
||||
# bug #6438
|
||||
doAssert($ %*[] == "[]")
|
||||
doAssert($ %*{} == "{}")
|
||||
|
||||
echo("Tests succeeded!")
|
||||
|
|
|
|||
|
|
@ -498,7 +498,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
|||
# Cannot use INET6_ADDRSTRLEN here, because it's a C define.
|
||||
var buf: array[64, char]
|
||||
if inet_ntop(name.sin6_family.cint,
|
||||
addr name, buf.cstring, sizeof(buf).int32).isNil:
|
||||
addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
|
||||
raiseOSError(osLastError())
|
||||
result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
|
||||
else:
|
||||
|
|
@ -534,7 +534,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
|||
# Cannot use INET6_ADDRSTRLEN here, because it's a C define.
|
||||
var buf: array[64, char]
|
||||
if inet_ntop(name.sin6_family.cint,
|
||||
addr name, buf.cstring, sizeof(buf).int32).isNil:
|
||||
addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
|
||||
raiseOSError(osLastError())
|
||||
result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
## A value of type ``Option[T]`` either contains a value `x` (represented as
|
||||
## ``some(x)``) or is empty (``none(T)``).
|
||||
##
|
||||
## This can be useful when you have a value that can be present or not. The
|
||||
## This can be useful when you have a value that can be present or not. The
|
||||
## absence of a value is often represented by ``nil``, but it is not always
|
||||
## available, nor is it always a good solution.
|
||||
##
|
||||
|
|
@ -67,10 +67,8 @@
|
|||
## assert(false) # This will not be reached
|
||||
## except UnpackError: # Because an exception is raised
|
||||
## discard
|
||||
|
||||
import typetraits
|
||||
|
||||
|
||||
type
|
||||
Option*[T] = object
|
||||
## An optional type that stores its value and state separately in a boolean.
|
||||
|
|
@ -78,7 +76,6 @@ type
|
|||
has: bool
|
||||
UnpackError* = ref object of ValueError
|
||||
|
||||
|
||||
proc some*[T](val: T): Option[T] =
|
||||
## Returns a ``Option`` that has this value.
|
||||
result.has = true
|
||||
|
|
@ -88,14 +85,12 @@ proc none*(T: typedesc): Option[T] =
|
|||
## Returns a ``Option`` for this type that has no value.
|
||||
result.has = false
|
||||
|
||||
|
||||
proc isSome*[T](self: Option[T]): bool =
|
||||
self.has
|
||||
|
||||
proc isNone*[T](self: Option[T]): bool =
|
||||
not self.has
|
||||
|
||||
|
||||
proc unsafeGet*[T](self: Option[T]): T =
|
||||
## Returns the value of a ``some``. Behavior is undefined for ``none``.
|
||||
assert self.isSome
|
||||
|
|
@ -110,12 +105,11 @@ proc get*[T](self: Option[T]): T =
|
|||
|
||||
proc get*[T](self: Option[T], otherwise: T): T =
|
||||
## Returns the contents of this option or `otherwise` if the option is none.
|
||||
if self.isSome:
|
||||
if self.has:
|
||||
self.val
|
||||
else:
|
||||
otherwise
|
||||
|
||||
|
||||
proc map*[T](self: Option[T], callback: proc (input: T)) =
|
||||
## Applies a callback to the value in this Option
|
||||
if self.has:
|
||||
|
|
@ -123,12 +117,27 @@ proc map*[T](self: Option[T], callback: proc (input: T)) =
|
|||
|
||||
proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] =
|
||||
## Applies a callback to the value in this Option and returns an option
|
||||
## containing the new value. If this option is None, None will be returned
|
||||
## containing the new value. If this option is None, None will be returned.
|
||||
if self.has:
|
||||
some[R]( callback(self.val) )
|
||||
some[R](callback(self.val))
|
||||
else:
|
||||
none(R)
|
||||
|
||||
proc flatten*[A](self: Option[Option[A]]): Option[A] =
|
||||
## Remove one level of structure in a nested Option.
|
||||
if self.has:
|
||||
self.val
|
||||
else:
|
||||
none(A)
|
||||
|
||||
proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Option[B] =
|
||||
## Applies a callback to the value in this Option and returns an
|
||||
## option containing the new value. If this option is None, None will be
|
||||
## returned. Similar to ``map``, with the difference that the callback
|
||||
## returns an Option, not a raw value. This allows multiple procs with a
|
||||
## signature of ``A -> Option[B]`` (including A = B) to be chained together.
|
||||
map(self, callback).flatten()
|
||||
|
||||
proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
|
||||
## Applies a callback to the value in this Option. If the callback returns
|
||||
## `true`, the option is returned as a Some. If it returns false, it is
|
||||
|
|
@ -138,21 +147,18 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
|
|||
else:
|
||||
self
|
||||
|
||||
|
||||
proc `==`*(a, b: Option): bool =
|
||||
## Returns ``true`` if both ``Option``s are ``none``,
|
||||
## or if they have equal values
|
||||
(a.has and b.has and a.val == b.val) or (not a.has and not b.has)
|
||||
|
||||
|
||||
proc `$`*[T]( self: Option[T] ): string =
|
||||
proc `$`*[T](self: Option[T]): string =
|
||||
## Returns the contents of this option or `otherwise` if the option is none.
|
||||
if self.has:
|
||||
"Some(" & $self.val & ")"
|
||||
else:
|
||||
"None[" & T.name & "]"
|
||||
|
||||
|
||||
when isMainModule:
|
||||
import unittest, sequtils
|
||||
|
||||
|
|
@ -198,12 +204,12 @@ when isMainModule:
|
|||
check false
|
||||
|
||||
test "get with a default value":
|
||||
check( some("Correct").get("Wrong") == "Correct" )
|
||||
check( stringNone.get("Correct") == "Correct" )
|
||||
check(some("Correct").get("Wrong") == "Correct")
|
||||
check(stringNone.get("Correct") == "Correct")
|
||||
|
||||
test "$":
|
||||
check( $(some("Correct")) == "Some(Correct)" )
|
||||
check( $(stringNone) == "None[string]" )
|
||||
check($(some("Correct")) == "Some(Correct)")
|
||||
check($(stringNone) == "None[string]")
|
||||
|
||||
test "map with a void result":
|
||||
var procRan = 0
|
||||
|
|
@ -212,11 +218,38 @@ when isMainModule:
|
|||
intNone.map(proc (v: int) = check false)
|
||||
|
||||
test "map":
|
||||
check( some(123).map(proc (v: int): int = v * 2) == some(246) )
|
||||
check( intNone.map(proc (v: int): int = v * 2).isNone )
|
||||
check(some(123).map(proc (v: int): int = v * 2) == some(246))
|
||||
check(intNone.map(proc (v: int): int = v * 2).isNone)
|
||||
|
||||
test "filter":
|
||||
check( some(123).filter(proc (v: int): bool = v == 123) == some(123) )
|
||||
check( some(456).filter(proc (v: int): bool = v == 123).isNone )
|
||||
check( intNone.filter(proc (v: int): bool = check false).isNone )
|
||||
check(some(123).filter(proc (v: int): bool = v == 123) == some(123))
|
||||
check(some(456).filter(proc (v: int): bool = v == 123).isNone)
|
||||
check(intNone.filter(proc (v: int): bool = check false).isNone)
|
||||
|
||||
test "flatMap":
|
||||
proc addOneIfNotZero(v: int): Option[int] =
|
||||
if v != 0:
|
||||
result = some(v + 1)
|
||||
else:
|
||||
result = none(int)
|
||||
|
||||
check(some(1).flatMap(addOneIfNotZero) == some(2))
|
||||
check(some(0).flatMap(addOneIfNotZero) == none(int))
|
||||
check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3))
|
||||
|
||||
proc maybeToString(v: int): Option[string] =
|
||||
if v != 0:
|
||||
result = some($v)
|
||||
else:
|
||||
result = none(string)
|
||||
|
||||
check(some(1).flatMap(maybeToString) == some("1"))
|
||||
|
||||
proc maybeExclaim(v: string): Option[string] =
|
||||
if v != "":
|
||||
result = some v & "!"
|
||||
else:
|
||||
result = none(string)
|
||||
|
||||
check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
|
||||
check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string))
|
||||
|
|
|
|||
|
|
@ -410,13 +410,11 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
result.readDataImpl = hsReadData
|
||||
result.writeDataImpl = hsWriteData
|
||||
|
||||
proc buildCommandLine(a: string, args: openArray[string]): cstring =
|
||||
var res = quoteShell(a)
|
||||
proc buildCommandLine(a: string, args: openArray[string]): string =
|
||||
result = quoteShell(a)
|
||||
for i in 0..high(args):
|
||||
res.add(' ')
|
||||
res.add(quoteShell(args[i]))
|
||||
result = cast[cstring](alloc0(res.len+1))
|
||||
copyMem(result, cstring(res), res.len)
|
||||
result.add(' ')
|
||||
result.add(quoteShell(args[i]))
|
||||
|
||||
proc buildEnv(env: StringTableRef): tuple[str: cstring, len: int] =
|
||||
var L = 0
|
||||
|
|
@ -540,11 +538,13 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
result.errHandle = FileHandle(si.hStdError)
|
||||
|
||||
var cmdl: cstring
|
||||
var cmdRoot: string
|
||||
if poEvalCommand in options:
|
||||
cmdl = command
|
||||
assert args.len == 0
|
||||
else:
|
||||
cmdl = buildCommandLine(command, args)
|
||||
cmdRoot = buildCommandLine(command, args)
|
||||
cmdl = cstring(cmdRoot)
|
||||
var wd: cstring = nil
|
||||
var e = (str: nil.cstring, len: -1)
|
||||
if len(workingDir) > 0: wd = workingDir
|
||||
|
|
|
|||
|
|
@ -888,7 +888,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
|
|||
n = x
|
||||
result = newString(len)
|
||||
for j in countdown(len-1, 0):
|
||||
result[j] = HexChars[(n and 0xF).int]
|
||||
result[j] = HexChars[int(n and 0xF)]
|
||||
n = n shr 4
|
||||
# handle negative overflow
|
||||
if n == 0 and x < 0: n = -1
|
||||
|
|
|
|||
|
|
@ -509,10 +509,6 @@ macro check*(conditions: untyped): untyped =
|
|||
## "AKB48".toLowerAscii() == "akb48"
|
||||
## 'C' in teams
|
||||
let checked = callsite()[1]
|
||||
var
|
||||
argsAsgns = newNimNode(nnkStmtList)
|
||||
argsPrintOuts = newNimNode(nnkStmtList)
|
||||
counter = 0
|
||||
|
||||
template asgn(a: untyped, value: typed) =
|
||||
var a = value # XXX: we need "var: var" here in order to
|
||||
|
|
@ -522,66 +518,71 @@ macro check*(conditions: untyped): untyped =
|
|||
when compiles(string($value)):
|
||||
checkpoint(name & " was " & $value)
|
||||
|
||||
proc inspectArgs(exp: NimNode): NimNode =
|
||||
result = copyNimTree(exp)
|
||||
proc inspectArgs(exp: NimNode): tuple[assigns, check, printOuts: NimNode] =
|
||||
result.check = copyNimTree(exp)
|
||||
result.assigns = newNimNode(nnkStmtList)
|
||||
result.printOuts = newNimNode(nnkStmtList)
|
||||
|
||||
var counter = 0
|
||||
|
||||
if exp[0].kind == nnkIdent and
|
||||
$exp[0] in ["and", "or", "not", "in", "notin", "==", "<=",
|
||||
$exp[0] in ["not", "in", "notin", "==", "<=",
|
||||
">=", "<", ">", "!=", "is", "isnot"]:
|
||||
for i in countup(1, exp.len - 1):
|
||||
|
||||
for i in 1 ..< exp.len:
|
||||
if exp[i].kind notin nnkLiterals:
|
||||
inc counter
|
||||
var arg = newIdentNode(":p" & $counter)
|
||||
var argStr = exp[i].toStrLit
|
||||
var paramAst = exp[i]
|
||||
let argStr = exp[i].toStrLit
|
||||
let paramAst = exp[i]
|
||||
if exp[i].kind == nnkIdent:
|
||||
argsPrintOuts.add getAst(print(argStr, paramAst))
|
||||
if exp[i].kind in nnkCallKinds:
|
||||
var callVar = newIdentNode(":c" & $counter)
|
||||
argsAsgns.add getAst(asgn(callVar, paramAst))
|
||||
result[i] = callVar
|
||||
argsPrintOuts.add getAst(print(argStr, callVar))
|
||||
result.printOuts.add getAst(print(argStr, paramAst))
|
||||
if exp[i].kind in nnkCallKinds + { nnkDotExpr, nnkBracketExpr }:
|
||||
let callVar = newIdentNode(":c" & $counter)
|
||||
result.assigns.add getAst(asgn(callVar, paramAst))
|
||||
result.check[i] = callVar
|
||||
result.printOuts.add getAst(print(argStr, callVar))
|
||||
if exp[i].kind == nnkExprEqExpr:
|
||||
# ExprEqExpr
|
||||
# Ident !"v"
|
||||
# IntLit 2
|
||||
result[i] = exp[i][1]
|
||||
result.check[i] = exp[i][1]
|
||||
if exp[i].typekind notin {ntyTypeDesc}:
|
||||
argsAsgns.add getAst(asgn(arg, paramAst))
|
||||
argsPrintOuts.add getAst(print(argStr, arg))
|
||||
let arg = newIdentNode(":p" & $counter)
|
||||
result.assigns.add getAst(asgn(arg, paramAst))
|
||||
result.printOuts.add getAst(print(argStr, arg))
|
||||
if exp[i].kind != nnkExprEqExpr:
|
||||
result[i] = arg
|
||||
result.check[i] = arg
|
||||
else:
|
||||
result[i][1] = arg
|
||||
result.check[i][1] = arg
|
||||
|
||||
case checked.kind
|
||||
of nnkCallKinds:
|
||||
template rewrite(call, lineInfoLit, callLit,
|
||||
argAssgs, argPrintOuts) =
|
||||
block:
|
||||
argAssgs #all callables (and assignments) are run here
|
||||
if not call:
|
||||
checkpoint(lineInfoLit & ": Check failed: " & callLit)
|
||||
argPrintOuts
|
||||
fail()
|
||||
|
||||
var checkedStr = checked.toStrLit
|
||||
let parameterizedCheck = inspectArgs(checked)
|
||||
result = getAst(rewrite(parameterizedCheck, checked.lineinfo, checkedStr,
|
||||
argsAsgns, argsPrintOuts))
|
||||
let (assigns, check, printOuts) = inspectArgs(checked)
|
||||
let lineinfo = newStrLitNode(checked.lineinfo)
|
||||
let callLit = checked.toStrLit
|
||||
result = quote do:
|
||||
block:
|
||||
`assigns`
|
||||
if not `check`:
|
||||
checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
|
||||
`printOuts`
|
||||
fail()
|
||||
|
||||
of nnkStmtList:
|
||||
result = newNimNode(nnkStmtList)
|
||||
for i in countup(0, checked.len - 1):
|
||||
if checked[i].kind != nnkCommentStmt:
|
||||
result.add(newCall(!"check", checked[i]))
|
||||
for node in checked:
|
||||
if node.kind != nnkCommentStmt:
|
||||
result.add(newCall(!"check", node))
|
||||
|
||||
else:
|
||||
template rewrite(exp, lineInfoLit, expLit) =
|
||||
if not exp:
|
||||
checkpoint(lineInfoLit & ": Check failed: " & expLit)
|
||||
fail()
|
||||
let lineinfo = newStrLitNode(checked.lineinfo)
|
||||
let callLit = checked.toStrLit
|
||||
|
||||
result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
|
||||
result = quote do:
|
||||
if not `checked`:
|
||||
checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
|
||||
fail()
|
||||
|
||||
template require*(conditions: untyped) =
|
||||
## Same as `check` except any failed test causes the program to quit
|
||||
|
|
|
|||
|
|
@ -246,6 +246,9 @@ type
|
|||
UncheckedArray* {.unchecked.}[T] = array[0, T]
|
||||
## Array with no bounds checking
|
||||
|
||||
when defined(nimHasOpt):
|
||||
type opt*{.magic: "Opt".}[T]
|
||||
|
||||
proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of an array, a sequence, a string or
|
||||
## the highest possible value of an ordinal value `x`. As a special
|
||||
|
|
@ -409,8 +412,7 @@ when not defined(JS):
|
|||
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
template space(s: PGenericSeq): int {.dirty.} =
|
||||
s.reserved and not seqShallowFlag
|
||||
|
||||
s.reserved and not (seqShallowFlag or strlitFlag)
|
||||
include "system/hti"
|
||||
|
||||
type
|
||||
|
|
@ -718,7 +720,7 @@ proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
|
|||
magic: "LengthOpenArray", noSideEffect.}
|
||||
proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
|
||||
proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.}
|
||||
proc len*[I, T](x: array[I, T]): int {.magic: "LengthArray", noSideEffect.}
|
||||
proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.}
|
||||
proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
|
||||
## returns the length of an array, an openarray, a sequence or a string.
|
||||
## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is
|
||||
|
|
@ -1329,6 +1331,9 @@ const
|
|||
## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el".
|
||||
|
||||
seqShallowFlag = low(int)
|
||||
strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \
|
||||
# emit this flag
|
||||
# for string literals, it allows for some optimizations.
|
||||
|
||||
{.push profiler: off.}
|
||||
when defined(nimKnowsNimvm):
|
||||
|
|
@ -1435,7 +1440,12 @@ when defined(nimdoc):
|
|||
|
||||
elif defined(genode):
|
||||
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn,
|
||||
importcpp: "genodeEnv->parent().exit(@)", header: "<base/env.h>".}
|
||||
importcpp: "genodeEnv->parent().exit(@); Genode::sleep_forever()",
|
||||
header: "<base/sleep.h>".}
|
||||
|
||||
elif defined(nodejs):
|
||||
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
|
||||
importc: "process.exit", noreturn.}
|
||||
|
||||
else:
|
||||
proc quit*(errorcode: int = QuitSuccess) {.
|
||||
|
|
@ -3402,10 +3412,10 @@ when hasAlloc or defined(nimscript):
|
|||
proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
|
||||
## slice operation for strings.
|
||||
## returns the inclusive range [s[x.a], s[x.b]]:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = "abcdef"
|
||||
## assert s[1..3] == "bcd"
|
||||
## assert s[1..3] == "bcd"
|
||||
result = s.substr(x.a, x.b)
|
||||
|
||||
proc `[]=`*(s: var string, x: Slice[int], b: string) =
|
||||
|
|
@ -3427,7 +3437,7 @@ when hasAlloc or defined(nimscript):
|
|||
proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
|
||||
## slice operation for arrays.
|
||||
## returns the inclusive range [a[x.a], a[x.b]]:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var a = [1,2,3,4]
|
||||
## assert a[0..2] == @[1,2,3]
|
||||
|
|
@ -3466,7 +3476,7 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
|
|||
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
|
||||
## slice operation for sequences.
|
||||
## returns the inclusive range [s[x.a], s[x.b]]:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = @[1,2,3,4]
|
||||
## assert s[0..2] == @[1,2,3]
|
||||
|
|
@ -3719,7 +3729,9 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
|
|||
## purposes.
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
var s = cast[PGenericSeq](s)
|
||||
s.reserved = s.reserved or seqShallowFlag
|
||||
# string literals cannot become 'shallow':
|
||||
if (s.reserved and strlitFlag) == 0:
|
||||
s.reserved = s.reserved or seqShallowFlag
|
||||
|
||||
type
|
||||
NimNodeObj = object
|
||||
|
|
|
|||
|
|
@ -63,12 +63,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
|
|||
sysAssert(dest != nil, "genericAssignAux 3")
|
||||
unsureAsgnRef(x, newSeq(mt, seq.len))
|
||||
var dst = cast[ByteAddress](cast[PPointer](dest)[])
|
||||
for i in 0..seq.len-1:
|
||||
genericAssignAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base, shallow)
|
||||
if ntfNoRefs in mt.base.flags:
|
||||
copyMem(cast[pointer](dst +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize),
|
||||
seq.len * mt.base.size)
|
||||
else:
|
||||
for i in 0..seq.len-1:
|
||||
genericAssignAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base, shallow)
|
||||
of tyObject:
|
||||
if mt.base != nil:
|
||||
genericAssignAux(dest, src, mt.base, shallow)
|
||||
|
|
@ -89,6 +94,19 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
|
|||
cast[pointer](s +% i*% mt.base.size), mt.base, shallow)
|
||||
of tyRef:
|
||||
unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[])
|
||||
of tyOptAsRef:
|
||||
let s2 = cast[PPointer](src)[]
|
||||
let d = cast[PPointer](dest)
|
||||
if s2 == nil:
|
||||
unsureAsgnRef(d, s2)
|
||||
else:
|
||||
when declared(usrToCell):
|
||||
let realType = usrToCell(s2).typ
|
||||
else:
|
||||
let realType = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[]
|
||||
else: mt.base
|
||||
var z = newObj(realType, realType.base.size)
|
||||
genericAssignAux(d, addr z, mt.base, shallow)
|
||||
else:
|
||||
copyMem(dest, src, mt.size) # copy raw bits
|
||||
|
||||
|
|
@ -115,6 +133,7 @@ when false:
|
|||
of tyPtr: k = "ptr"
|
||||
of tyRef: k = "ref"
|
||||
of tyVar: k = "var"
|
||||
of tyOptAsRef: k = "optref"
|
||||
of tySequence: k = "seq"
|
||||
of tyProc: k = "proc"
|
||||
of tyPointer: k = "range"
|
||||
|
|
@ -195,7 +214,7 @@ proc genericReset(dest: pointer, mt: PNimType) =
|
|||
var d = cast[ByteAddress](dest)
|
||||
sysAssert(mt != nil, "genericReset 2")
|
||||
case mt.kind
|
||||
of tyString, tyRef, tySequence:
|
||||
of tyString, tyRef, tyOptAsRef, tySequence:
|
||||
unsureAsgnRef(cast[PPointer](dest), nil)
|
||||
of tyTuple:
|
||||
genericResetAux(dest, mt.node)
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
|||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
storeAux(cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
|
||||
of tyRef:
|
||||
of tyRef, tyOptAsRef:
|
||||
var s = cast[PPointer](src)[]
|
||||
var x = cast[PPointer](dest)
|
||||
if s == nil:
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
|
|||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, tab)
|
||||
of tyRef:
|
||||
of tyRef, tyOptAsRef:
|
||||
let s2 = cast[PPointer](src)[]
|
||||
if s2 == nil:
|
||||
unsureAsgnRef(cast[PPointer](dest), s2)
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
|
|||
for i in 0..n.len-1:
|
||||
# inlined for speed
|
||||
if n.sons[i].kind == nkSlot:
|
||||
if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
|
||||
if n.sons[i].typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}:
|
||||
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
|
||||
else:
|
||||
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
|
||||
|
|
@ -366,7 +366,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
|||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
|
|
@ -379,13 +379,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
|||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
|
||||
gcAssert(cell.typ != nil, "forAllChildren: 3")
|
||||
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
let marker = cell.typ.marker
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
of tyRef, tyOptAsRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
|
|
@ -461,7 +461,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
|||
incTypeSize typ, size
|
||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||
acquire(gch)
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
#gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
|
||||
|
|
@ -509,7 +509,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
|||
incTypeSize typ, size
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 begin")
|
||||
acquire(gch)
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
|
||||
|
||||
|
|
@ -945,10 +945,10 @@ when not defined(useNimRtl):
|
|||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
|
||||
when nimCoroutines:
|
||||
result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
for stack in items(gch.stack):
|
||||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
|
||||
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
|
||||
{.pop.} # profiler: off, stackTrace: off
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -9,17 +9,19 @@
|
|||
|
||||
# Garbage Collector
|
||||
#
|
||||
# The basic algorithm is *Deferred Reference Counting* with an incremental mark
|
||||
# The basic algorithm is an incremental mark
|
||||
# and sweep GC to free cycles. It is hard realtime in that if you play
|
||||
# according to its rules, no deadline will ever be missed.
|
||||
|
||||
# XXX Ensure by smart color masking that the object is not in the ZCT.
|
||||
# Since this kind of collector is very bad at recycling dead objects
|
||||
# early, Nim's codegen emits ``nimEscape`` calls at strategic
|
||||
# places. For this to work even 'unsureAsgnRef' needs to mark things
|
||||
# so that only return values need to be considered in ``nimEscape``.
|
||||
|
||||
{.push profiler:off.}
|
||||
|
||||
const
|
||||
CycleIncrease = 2 # is a multiplicative increase
|
||||
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
|
||||
InitialCycleThreshold = 512*1024 # start collecting after 500KB
|
||||
ZctThreshold = 500 # we collect garbage if the ZCT's size
|
||||
# reaches this threshold
|
||||
# this seems to be a good value
|
||||
|
|
@ -40,13 +42,11 @@ type
|
|||
iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
|
||||
|
||||
const
|
||||
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
|
||||
escapedBit = 0b1000 # so that lowest 3 bits are not touched
|
||||
rcBlackOrig = 0b000
|
||||
rcWhiteOrig = 0b001
|
||||
rcGrey = 0b010 # traditional color for incremental mark&sweep
|
||||
rcUnused = 0b011
|
||||
ZctFlag = 0b100 # in ZCT
|
||||
rcShift = 3 # shift by rcShift to get the reference counter
|
||||
colorMask = 0b011
|
||||
type
|
||||
WalkOp = enum
|
||||
|
|
@ -63,13 +63,13 @@ type
|
|||
|
||||
GcStat = object
|
||||
stackScans: int # number of performed stack scans (for statistics)
|
||||
cycleCollections: int # number of performed full collections
|
||||
completedCollections: int # number of performed full collections
|
||||
maxThreshold: int # max threshold that has been set
|
||||
maxStackSize: int # max stack size
|
||||
maxStackCells: int # max stack cells in ``decStack``
|
||||
cycleTableSize: int # max entries in cycle table
|
||||
maxPause: int64 # max measured GC pause in nanoseconds
|
||||
|
||||
|
||||
GcStack {.final, pure.} = object
|
||||
when nimCoroutines:
|
||||
prev: ptr GcStack
|
||||
|
|
@ -93,15 +93,13 @@ type
|
|||
cycleThreshold: int
|
||||
when useCellIds:
|
||||
idGenerator: int
|
||||
zct: CellSeq # the zero count table
|
||||
decStack: CellSeq # cells in the stack that are to decref again
|
||||
greyStack: CellSeq
|
||||
recGcLock: int # prevent recursion via finalizers; no thread lock
|
||||
when withRealTime:
|
||||
maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
|
||||
region: MemRegion # garbage collected region
|
||||
stat: GcStat
|
||||
additionalRoots: CellSeq # dummy roots for GC_ref/unref
|
||||
additionalRoots: CellSeq # explicit roots for GC_ref/unref
|
||||
spaceIter: ObjectSpaceIter
|
||||
pDumpHeapFile: pointer # File that is used for GC_dumpHeap
|
||||
when hasThreadSupport:
|
||||
|
|
@ -113,19 +111,25 @@ var
|
|||
when not defined(useNimRtl):
|
||||
instantiateForRegion(gch.region)
|
||||
|
||||
template acquire(gch: GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
acquireSys(HeapLock)
|
||||
|
||||
template release(gch: GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
releaseSys(HeapLock)
|
||||
|
||||
proc initGC() =
|
||||
when not defined(useNimRtl):
|
||||
gch.red = (1-gch.black)
|
||||
gch.cycleThreshold = InitialCycleThreshold
|
||||
gch.stat.stackScans = 0
|
||||
gch.stat.cycleCollections = 0
|
||||
gch.stat.completedCollections = 0
|
||||
gch.stat.maxThreshold = 0
|
||||
gch.stat.maxStackSize = 0
|
||||
gch.stat.maxStackCells = 0
|
||||
gch.stat.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.decStack)
|
||||
init(gch.additionalRoots)
|
||||
init(gch.greyStack)
|
||||
when hasThreadSupport:
|
||||
|
|
@ -147,11 +151,6 @@ template gcAssert(cond: bool, msg: string) =
|
|||
writeStackTrace()
|
||||
quit 1
|
||||
|
||||
proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
|
||||
if (c.refcount and ZctFlag) == 0:
|
||||
c.refcount = c.refcount or ZctFlag
|
||||
add(s, c)
|
||||
|
||||
proc cellToUsr(cell: PCell): pointer {.inline.} =
|
||||
# convert object (=pointer to refcount) to pointer to userdata
|
||||
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
|
||||
|
|
@ -168,7 +167,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
|
|||
result = usrToCell(c).typ
|
||||
|
||||
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||
result = int(usrToCell(p).refcount) shr rcShift
|
||||
result = 0
|
||||
|
||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||
|
|
@ -178,6 +177,12 @@ template color(c): expr = c.refCount and colorMask
|
|||
template setColor(c, col) =
|
||||
c.refcount = c.refcount and not colorMask or col
|
||||
|
||||
template markAsEscaped(c: PCell) =
|
||||
c.refcount = c.refcount or escapedBit
|
||||
|
||||
template didEscape(c: PCell): bool =
|
||||
(c.refCount and escapedBit) != 0
|
||||
|
||||
proc writeCell(file: File; msg: cstring, c: PCell) =
|
||||
var kind = -1
|
||||
if c.typ != nil: kind = ord(c.typ.kind)
|
||||
|
|
@ -189,18 +194,18 @@ proc writeCell(file: File; msg: cstring, c: PCell) =
|
|||
else:
|
||||
let id = c
|
||||
when leakDetector:
|
||||
c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n",
|
||||
msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line)
|
||||
c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n",
|
||||
msg, id, kind, didEscape(c), col, c.filename, c.line)
|
||||
else:
|
||||
c_fprintf(file, "%s %p %d rc=%ld color=%c\n",
|
||||
msg, id, kind, c.refcount shr rcShift, col)
|
||||
c_fprintf(file, "%s %p %d escaped=%ld color=%c\n",
|
||||
msg, id, kind, didEscape(c), col)
|
||||
|
||||
proc writeCell(msg: cstring, c: PCell) =
|
||||
stdout.writeCell(msg, c)
|
||||
|
||||
proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.}
|
||||
|
||||
template gcTrace(cell, state: expr): stmt {.immediate.} =
|
||||
template gcTrace(cell, state: untyped) =
|
||||
when traceGC: writeCell(myastToStr(state), cell)
|
||||
|
||||
# forward declarations:
|
||||
|
|
@ -211,52 +216,17 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
|
|||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
|
||||
# we need the prototype here for debugging purposes
|
||||
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement
|
||||
template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
|
||||
else:
|
||||
template `--`(x: expr): expr =
|
||||
dec(x, rcIncrement)
|
||||
x <% rcIncrement
|
||||
template `++`(x: expr): stmt = inc(x, rcIncrement)
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
if cell.typ.finalizer != nil:
|
||||
# the finalizer could invoke something that
|
||||
# allocates memory; this could trigger a garbage
|
||||
# collection. Since we are already collecting we
|
||||
# prevend recursive entering here by a lock.
|
||||
# XXX: we should set the cell's children to nil!
|
||||
inc(gch.recGcLock)
|
||||
(cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||
dec(gch.recGcLock)
|
||||
|
||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
discard
|
||||
|
||||
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
addZCT(gch.zct, c)
|
||||
|
||||
proc decRef(c: PCell) {.inline.} =
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
|
||||
gcAssert(c.refcount >=% rcIncrement, "decRef")
|
||||
if --c.refcount:
|
||||
rtlAddZCT(c)
|
||||
|
||||
proc incRef(c: PCell) {.inline.} =
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
|
||||
c.refcount = c.refcount +% rcIncrement
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerProc.} =
|
||||
let cell = usrToCell(p)
|
||||
incRef(cell)
|
||||
markAsEscaped(cell)
|
||||
add(gch.additionalRoots, cell)
|
||||
|
||||
proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||
let cell = usrToCell(p)
|
||||
decRef(cell)
|
||||
var L = gch.additionalRoots.len-1
|
||||
var i = L
|
||||
let d = gch.additionalRoots.d
|
||||
|
|
@ -267,6 +237,12 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
|
|||
break
|
||||
dec(i)
|
||||
|
||||
proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
|
||||
discard "can we do some freeing here?"
|
||||
|
||||
proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
|
||||
discard "can we do some freeing here?"
|
||||
|
||||
template markGrey(x: PCell) =
|
||||
if x.color != 1-gch.black and gch.phase == Phase.Marking:
|
||||
if not isAllocatedPtr(gch.region, x):
|
||||
|
|
@ -280,59 +256,32 @@ proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
|
|||
## adds 'p' to the cycle candidate set for the cycle collector. It is
|
||||
## necessary if you used the 'acyclic' pragma for optimization
|
||||
## purposes and need to break cycles manually.
|
||||
rtlAddCycleRoot(usrToCell(cast[pointer](p)))
|
||||
discard
|
||||
|
||||
proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
|
||||
sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle")
|
||||
var c = usrToCell(p)
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr")
|
||||
if --c.refcount:
|
||||
rtlAddZCT(c)
|
||||
sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
|
||||
sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
|
||||
|
||||
proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc!
|
||||
template asgnRefImpl =
|
||||
gcAssert(not isOnStack(dest), "asgnRef")
|
||||
# BUGFIX: first incRef then decRef!
|
||||
if src != nil:
|
||||
let s = usrToCell(src)
|
||||
incRef(s)
|
||||
markAsEscaped(s)
|
||||
markGrey(s)
|
||||
if dest[] != nil: decRef(usrToCell(dest[]))
|
||||
dest[] = src
|
||||
|
||||
proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc!
|
||||
asgnRefImpl()
|
||||
|
||||
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc if it is known at compile time that no
|
||||
# cycle is possible.
|
||||
gcAssert(not isOnStack(dest), "asgnRefNoCycle")
|
||||
if src != nil:
|
||||
var c = usrToCell(src)
|
||||
++c.refcount
|
||||
markGrey(c)
|
||||
if dest[] != nil:
|
||||
var c = usrToCell(dest[])
|
||||
if --c.refcount:
|
||||
rtlAddZCT(c)
|
||||
dest[] = src
|
||||
asgnRefImpl()
|
||||
|
||||
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
|
||||
# unsureAsgnRef updates the reference counters only if dest is not on the
|
||||
# unsureAsgnRef marks 'src' as grey only if dest is not on the
|
||||
# stack. It is used by the code generator if it cannot decide wether a
|
||||
# reference is in the stack or not (this can happen for var parameters).
|
||||
if not isOnStack(dest):
|
||||
if src != nil:
|
||||
let s = usrToCell(src)
|
||||
incRef(s)
|
||||
markGrey(s)
|
||||
# XXX finally use assembler for the stack checking instead!
|
||||
# the test for '!= nil' is correct, but I got tired of the segfaults
|
||||
# resulting from the crappy stack checking:
|
||||
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
|
||||
else:
|
||||
# can't be an interior pointer if it's a stack location!
|
||||
gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
|
||||
"stack loc AND interior pointer")
|
||||
if src != nil:
|
||||
let s = usrToCell(src)
|
||||
markAsEscaped(s)
|
||||
if not isOnStack(dest): markGrey(s)
|
||||
dest[] = src
|
||||
|
||||
type
|
||||
|
|
@ -366,7 +315,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
|||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
|
|
@ -379,13 +328,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
|||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
|
||||
gcAssert(cell.typ != nil, "forAllChildren: 3")
|
||||
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
let marker = cell.typ.marker
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
of tyRef, tyOptAsRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
|
|
@ -396,50 +345,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
|||
GenericSeqSize), cell.typ.base, op)
|
||||
else: discard
|
||||
|
||||
proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
|
||||
# we check the last 8 entries (cache line) for a slot that could be reused.
|
||||
# In 63% of all cases we succeed here! But we have to optimize the heck
|
||||
# out of this small linear search so that ``newObj`` is not slowed down.
|
||||
#
|
||||
# Slots to try cache hit
|
||||
# 1 32%
|
||||
# 4 59%
|
||||
# 8 63%
|
||||
# 16 66%
|
||||
# all slots 68%
|
||||
var L = gch.zct.len
|
||||
var d = gch.zct.d
|
||||
when true:
|
||||
# loop unrolled for performance:
|
||||
template replaceZctEntry(i: expr) =
|
||||
c = d[i]
|
||||
if c.refcount >=% rcIncrement:
|
||||
c.refcount = c.refcount and not ZctFlag
|
||||
d[i] = res
|
||||
return
|
||||
if L > 8:
|
||||
var c: PCell
|
||||
replaceZctEntry(L-1)
|
||||
replaceZctEntry(L-2)
|
||||
replaceZctEntry(L-3)
|
||||
replaceZctEntry(L-4)
|
||||
replaceZctEntry(L-5)
|
||||
replaceZctEntry(L-6)
|
||||
replaceZctEntry(L-7)
|
||||
replaceZctEntry(L-8)
|
||||
add(gch.zct, res)
|
||||
else:
|
||||
d[L] = res
|
||||
inc(gch.zct.len)
|
||||
else:
|
||||
for i in countdown(L-1, max(0, L-8)):
|
||||
var c = d[i]
|
||||
if c.refcount >=% rcIncrement:
|
||||
c.refcount = c.refcount and not ZctFlag
|
||||
d[i] = res
|
||||
return
|
||||
add(gch.zct, res)
|
||||
|
||||
{.push stackTrace: off, profiler:off.}
|
||||
proc gcInvariant*() =
|
||||
sysAssert(allocInv(gch.region), "injected")
|
||||
|
|
@ -447,10 +352,12 @@ proc gcInvariant*() =
|
|||
markForDebug(gch)
|
||||
{.pop.}
|
||||
|
||||
include gc_common
|
||||
|
||||
proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
# generates a new object and sets its reference counter to 0
|
||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
|
|
@ -461,10 +368,8 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
|||
res.filename = framePtr.prev.filename
|
||||
res.line = framePtr.prev.line
|
||||
# refcount is zero, color is black, but mark it to be in the ZCT
|
||||
res.refcount = ZctFlag or allocColor()
|
||||
res.refcount = allocColor()
|
||||
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||
# its refcount is zero, so add it to the ZCT:
|
||||
addNewObjToZCT(res, gch)
|
||||
when logGC: writeCell("new cell", res)
|
||||
gcTrace(res, csAllocated)
|
||||
when useCellIds:
|
||||
|
|
@ -493,95 +398,38 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
|||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
# generates a new object and sets its reference counter to 1
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 begin")
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
|
||||
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
|
||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
# now it is buffered in the ZCT
|
||||
res.typ = typ
|
||||
when leakDetector:
|
||||
if framePtr != nil and framePtr.prev != nil:
|
||||
res.filename = framePtr.prev.filename
|
||||
res.line = framePtr.prev.line
|
||||
res.refcount = rcIncrement or allocColor() # refcount is 1
|
||||
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||
when logGC: writeCell("new cell", res)
|
||||
gcTrace(res, csAllocated)
|
||||
when useCellIds:
|
||||
inc gch.idGenerator
|
||||
res.id = gch.idGenerator
|
||||
result = cellToUsr(res)
|
||||
zeroMem(result, size)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 end")
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
result = newObj(typ, size)
|
||||
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
result = newObjRC1(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
result = newSeq(typ, len)
|
||||
|
||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||
acquire(gch)
|
||||
collectCT(gch)
|
||||
var ol = usrToCell(old)
|
||||
gcAssert(isAllocatedPtr(gch.region, ol), "growObj: freed pointer?")
|
||||
|
||||
sysAssert(ol.typ != nil, "growObj: 1")
|
||||
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
|
||||
sysAssert(allocInv(gch.region), "growObj begin")
|
||||
|
||||
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
|
||||
var elemSize = 1
|
||||
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
|
||||
incTypeSize ol.typ, newsize
|
||||
|
||||
let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||
copyMem(res, ol, oldsize + sizeof(Cell))
|
||||
zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
|
||||
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
|
||||
newsize-oldsize)
|
||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
|
||||
# This can be wrong for intermediate temps that are nevertheless on the
|
||||
# heap because of lambda lifting:
|
||||
#gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
|
||||
when logGC:
|
||||
writeCell("growObj old cell", ol)
|
||||
writeCell("growObj new cell", res)
|
||||
gcTrace(ol, csZctFreed)
|
||||
gcTrace(res, csAllocated)
|
||||
when reallyDealloc:
|
||||
sysAssert(allocInv(gch.region), "growObj before dealloc")
|
||||
if ol.refcount shr rcShift <=% 1:
|
||||
# free immediately to save space:
|
||||
if (ol.refcount and ZctFlag) != 0:
|
||||
var j = gch.zct.len-1
|
||||
var d = gch.zct.d
|
||||
while j >= 0:
|
||||
if d[j] == ol:
|
||||
d[j] = res
|
||||
break
|
||||
dec(j)
|
||||
rawDealloc(gch.region, ol)
|
||||
when false:
|
||||
# this is wrong since seqs can be shared via 'shallow':
|
||||
when reallyDealloc: rawDealloc(gch.region, ol)
|
||||
else:
|
||||
# we split the old refcount in 2 parts. XXX This is still not entirely
|
||||
# correct if the pointer that receives growObj's result is on the stack.
|
||||
# A better fix would be to emit the location specific write barrier for
|
||||
# 'growObj', but this is lots of more work and who knows what new problems
|
||||
# this would create.
|
||||
res.refcount = rcIncrement or allocColor()
|
||||
decRef(ol)
|
||||
else:
|
||||
sysAssert(ol.typ != nil, "growObj: 5")
|
||||
zeroMem(ol, sizeof(Cell))
|
||||
zeroMem(ol, sizeof(Cell))
|
||||
when useCellIds:
|
||||
inc gch.idGenerator
|
||||
res.id = gch.idGenerator
|
||||
release(gch)
|
||||
result = cellToUsr(res)
|
||||
sysAssert(allocInv(gch.region), "growObj end")
|
||||
when defined(memProfiler): nimProfile(newsize-oldsize)
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||
|
|
@ -637,12 +485,13 @@ proc GC_dumpHeap*(file: File) =
|
|||
## can be translated into "dot" syntax via the "heapdump2dot" tool.
|
||||
gch.pDumpHeapFile = file
|
||||
var spaceIter: ObjectSpaceIter
|
||||
var d = gch.decStack.d
|
||||
for i in 0 .. < gch.decStack.len:
|
||||
if isAllocatedPtr(gch.region, d[i]):
|
||||
c_fprintf(file, "onstack %p\n", d[i])
|
||||
else:
|
||||
c_fprintf(file, "onstack_invalid %p\n", d[i])
|
||||
when false:
|
||||
var d = gch.decStack.d
|
||||
for i in 0 .. < gch.decStack.len:
|
||||
if isAllocatedPtr(gch.region, d[i]):
|
||||
c_fprintf(file, "onstack %p\n", d[i])
|
||||
else:
|
||||
c_fprintf(file, "onstack_invalid %p\n", d[i])
|
||||
for i in 0 .. < globalMarkersLen: globalMarkers[i]()
|
||||
while true:
|
||||
let x = allObjectsAsProc(gch.region, addr spaceIter)
|
||||
|
|
@ -667,14 +516,6 @@ proc GC_dumpHeap() =
|
|||
|
||||
proc freeCyclicCell(gch: var GcHeap, c: PCell) =
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?")
|
||||
|
||||
var d = gch.decStack.d
|
||||
for i in 0..gch.decStack.len-1:
|
||||
if d[i] == c:
|
||||
writeCell("freeing ", c)
|
||||
GC_dumpHeap()
|
||||
gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!"
|
||||
|
||||
prepareDealloc(c)
|
||||
gcTrace(c, csCycFreed)
|
||||
when logGC: writeCell("cycle collector dealloc cell", c)
|
||||
|
|
@ -713,15 +554,6 @@ proc markRoot(gch: var GcHeap, c: PCell) {.inline.} =
|
|||
if c.color == 1-gch.black:
|
||||
c.setColor(rcGrey)
|
||||
add(gch.greyStack, c)
|
||||
elif c.color == rcGrey:
|
||||
var isGrey = false
|
||||
var d = gch.decStack.d
|
||||
for i in 0..gch.decStack.len-1:
|
||||
if d[i] == c:
|
||||
isGrey = true
|
||||
break
|
||||
if not isGrey:
|
||||
gcAssert false, "markRoot: root is already grey?!"
|
||||
|
||||
proc markIncremental(gch: var GcHeap): bool =
|
||||
var L = addr(gch.greyStack.len)
|
||||
|
|
@ -741,30 +573,14 @@ proc markIncremental(gch: var GcHeap): bool =
|
|||
c.setColor(gch.black)
|
||||
forAllChildren(c, waMarkGrey)
|
||||
elif c.color == (1-gch.black):
|
||||
gcAssert false, "wtf why are there white object in the greystack?"
|
||||
gcAssert false, "wtf why are there white objects in the greystack?"
|
||||
checkTime()
|
||||
gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
|
||||
|
||||
# assert that all local roots are black by now:
|
||||
var d = gch.decStack.d
|
||||
var errors = false
|
||||
for i in 0..gch.decStack.len-1:
|
||||
gcAssert(isAllocatedPtr(gch.region, d[i]), "markIncremental: isAllocatedPtr 2")
|
||||
if d[i].color != gch.black:
|
||||
writeCell("not black ", d[i])
|
||||
errors = true
|
||||
gcAssert(not errors, "wtf something wrong hre")
|
||||
result = true
|
||||
|
||||
proc markGlobals(gch: var GcHeap) =
|
||||
for i in 0 .. < globalMarkersLen: globalMarkers[i]()
|
||||
|
||||
proc markLocals(gch: var GcHeap) =
|
||||
var d = gch.decStack.d
|
||||
for i in 0 .. < gch.decStack.len:
|
||||
sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals"
|
||||
markRoot(gch, d[i])
|
||||
|
||||
proc doOperation(p: pointer, op: WalkOp) =
|
||||
if p == nil: return
|
||||
var c: PCell = usrToCell(p)
|
||||
|
|
@ -776,11 +592,7 @@ proc doOperation(p: pointer, op: WalkOp) =
|
|||
#if not isAllocatedPtr(gch.region, c):
|
||||
# c_fprintf(stdout, "[GC] decref bug: %p", c)
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
|
||||
gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
|
||||
#c.refcount = c.refcount -% rcIncrement
|
||||
when logGC: writeCell("decref (from doOperation)", c)
|
||||
decRef(c)
|
||||
#if c.refcount <% rcIncrement: addZCT(gch.zct, c)
|
||||
discard "use me for nimEscape?"
|
||||
of waMarkGlobal:
|
||||
template handleRoot =
|
||||
if gch.dumpHeapFile.isNil:
|
||||
|
|
@ -811,40 +623,6 @@ proc doOperation(p: pointer, op: WalkOp) =
|
|||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||
doOperation(d, WalkOp(op))
|
||||
|
||||
proc collectZCT(gch: var GcHeap): bool {.benign.}
|
||||
|
||||
proc collectCycles(gch: var GcHeap): bool =
|
||||
when hasThreadSupport:
|
||||
for c in gch.toDispose:
|
||||
nimGCunref(c)
|
||||
|
||||
# ensure the ZCT 'color' is not used:
|
||||
while gch.zct.len > 0: discard collectZCT(gch)
|
||||
|
||||
case gch.phase
|
||||
of Phase.None:
|
||||
gch.phase = Phase.Marking
|
||||
markGlobals(gch)
|
||||
|
||||
c_fprintf(stdout, "collectCycles: introduced bug E %ld\n", gch.phase)
|
||||
discard allocInv(gch.region)
|
||||
of Phase.Marking:
|
||||
# since locals do not have a write barrier, we need
|
||||
# to keep re-scanning them :-( but there is really nothing we can
|
||||
# do about that.
|
||||
markLocals(gch)
|
||||
if markIncremental(gch):
|
||||
gch.phase = Phase.Sweeping
|
||||
gch.red = 1 - gch.red
|
||||
of Phase.Sweeping:
|
||||
gcAssert gch.greyStack.len == 0, "greystack not empty"
|
||||
if sweep(gch):
|
||||
gch.phase = Phase.None
|
||||
# flip black/white meanings:
|
||||
gch.black = 1 - gch.black
|
||||
gcAssert gch.red == 1 - gch.black, "red color is wrong"
|
||||
result = true
|
||||
|
||||
proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
||||
# the addresses are not as cells on the stack, so turn them to cells:
|
||||
sysAssert(allocInv(gch.region), "gcMark begin")
|
||||
|
|
@ -855,62 +633,43 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
|||
var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
|
||||
if objStart != nil:
|
||||
# mark the cell:
|
||||
objStart.refcount = objStart.refcount +% rcIncrement
|
||||
add(gch.decStack, objStart)
|
||||
markRoot(gch, objStart)
|
||||
sysAssert(allocInv(gch.region), "gcMark end")
|
||||
|
||||
include gc_common
|
||||
|
||||
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
|
||||
forEachStackSlot(gch, gcMark)
|
||||
|
||||
proc collectZCT(gch: var GcHeap): bool =
|
||||
# Note: Freeing may add child objects to the ZCT! So essentially we do
|
||||
# deep freeing, which is bad for incremental operation. In order to
|
||||
# avoid a deep stack, we move objects to keep the ZCT small.
|
||||
# This is performance critical!
|
||||
var L = addr(gch.zct.len)
|
||||
takeStartTime(100)
|
||||
|
||||
while L[] > 0:
|
||||
var c = gch.zct.d[0]
|
||||
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
|
||||
# remove from ZCT:
|
||||
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
|
||||
|
||||
c.refcount = c.refcount and not ZctFlag
|
||||
gch.zct.d[0] = gch.zct.d[L[] - 1]
|
||||
dec(L[])
|
||||
takeTime()
|
||||
if c.refcount <% rcIncrement and c.color != rcGrey:
|
||||
# It may have a RC > 0, if it is in the hardware stack or
|
||||
# it has not been removed yet from the ZCT. This is because
|
||||
# ``incref`` does not bother to remove the cell from the ZCT
|
||||
# as this might be too slow.
|
||||
# In any case, it should be removed from the ZCT. But not
|
||||
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
|
||||
when logGC: writeCell("zct dealloc cell", c)
|
||||
gcTrace(c, csZctFreed)
|
||||
# We are about to free the object, call the finalizer BEFORE its
|
||||
# children are deleted as well, because otherwise the finalizer may
|
||||
# access invalid memory. This is done by prepareDealloc():
|
||||
prepareDealloc(c)
|
||||
forAllChildren(c, waZctDecRef)
|
||||
when reallyDealloc:
|
||||
sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
|
||||
rawDealloc(gch.region, c)
|
||||
else:
|
||||
sysAssert(c.typ != nil, "collectZCT 2")
|
||||
zeroMem(c, sizeof(Cell))
|
||||
checkTime()
|
||||
result = true
|
||||
|
||||
proc unmarkStackAndRegisters(gch: var GcHeap) =
|
||||
var d = gch.decStack.d
|
||||
for i in 0..gch.decStack.len-1:
|
||||
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
|
||||
decRef(d[i])
|
||||
gch.decStack.len = 0
|
||||
proc collectALittle(gch: var GcHeap): bool =
|
||||
case gch.phase
|
||||
of Phase.None:
|
||||
if getOccupiedMem(gch.region) >= gch.cycleThreshold:
|
||||
gch.phase = Phase.Marking
|
||||
markGlobals(gch)
|
||||
result = collectALittle(gch)
|
||||
#when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase)
|
||||
#discard allocInv(gch.region)
|
||||
of Phase.Marking:
|
||||
when hasThreadSupport:
|
||||
for c in gch.toDispose:
|
||||
nimGCunref(c)
|
||||
prepareForInteriorPointerChecking(gch.region)
|
||||
markStackAndRegisters(gch)
|
||||
inc(gch.stat.stackScans)
|
||||
if markIncremental(gch):
|
||||
gch.phase = Phase.Sweeping
|
||||
gch.red = 1 - gch.red
|
||||
of Phase.Sweeping:
|
||||
gcAssert gch.greyStack.len == 0, "greystack not empty"
|
||||
when hasThreadSupport:
|
||||
for c in gch.toDispose:
|
||||
nimGCunref(c)
|
||||
if sweep(gch):
|
||||
gch.phase = Phase.None
|
||||
# flip black/white meanings:
|
||||
gch.black = 1 - gch.black
|
||||
gcAssert gch.red == 1 - gch.black, "red color is wrong"
|
||||
inc(gch.stat.completedCollections)
|
||||
result = true
|
||||
|
||||
proc collectCTBody(gch: var GcHeap) =
|
||||
when withRealTime:
|
||||
|
|
@ -919,22 +678,12 @@ proc collectCTBody(gch: var GcHeap) =
|
|||
|
||||
when not nimCoroutines:
|
||||
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
|
||||
sysAssert(gch.decStack.len == 0, "collectCT")
|
||||
prepareForInteriorPointerChecking(gch.region)
|
||||
markStackAndRegisters(gch)
|
||||
gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
|
||||
inc(gch.stat.stackScans)
|
||||
if collectZCT(gch):
|
||||
when cycleGC:
|
||||
if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
|
||||
if collectCycles(gch):
|
||||
inc(gch.stat.cycleCollections)
|
||||
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
||||
CycleIncrease)
|
||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||
unmarkStackAndRegisters(gch)
|
||||
#gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
|
||||
if collectALittle(gch):
|
||||
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
||||
CycleIncrease)
|
||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||
sysAssert(allocInv(gch.region), "collectCT: end")
|
||||
|
||||
when withRealTime:
|
||||
let duration = getticks() - t0
|
||||
gch.stat.maxPause = max(gch.stat.maxPause, duration)
|
||||
|
|
@ -955,7 +704,7 @@ proc collectCT(gch: var GcHeap) =
|
|||
let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
|
||||
else:
|
||||
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
|
||||
if (gch.zct.len >= stackMarkCosts or (cycleGC and
|
||||
if (gch.greyStack.len >= stackMarkCosts or (cycleGC and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
|
||||
gch.recGcLock == 0:
|
||||
collectCTBody(gch)
|
||||
|
|
@ -969,10 +718,9 @@ when withRealTime:
|
|||
|
||||
proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
|
||||
gch.maxPause = us.toNano
|
||||
if (gch.zct.len >= ZctThreshold or (cycleGC and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
|
||||
strongAdvice:
|
||||
collectCTBody(gch)
|
||||
#if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or
|
||||
# alwaysGC or strongAdvice:
|
||||
collectCTBody(gch)
|
||||
|
||||
proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
|
||||
if stackSize >= 0:
|
||||
|
|
@ -1010,12 +758,8 @@ when not defined(useNimRtl):
|
|||
proc GC_setStrategy(strategy: GC_Strategy) =
|
||||
discard
|
||||
|
||||
proc GC_enableMarkAndSweep() =
|
||||
gch.cycleThreshold = InitialCycleThreshold
|
||||
|
||||
proc GC_disableMarkAndSweep() =
|
||||
gch.cycleThreshold = high(gch.cycleThreshold)-1
|
||||
# set to the max value to suppress the cycle detector
|
||||
proc GC_enableMarkAndSweep() = discard
|
||||
proc GC_disableMarkAndSweep() = discard
|
||||
|
||||
proc GC_fullCollect() =
|
||||
var oldThreshold = gch.cycleThreshold
|
||||
|
|
@ -1029,17 +773,17 @@ when not defined(useNimRtl):
|
|||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
||||
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
||||
"[GC] completed collections: " & $gch.stat.completedCollections & "\n" &
|
||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
||||
"[GC] grey stack capacity: " & $gch.greyStack.cap & "\n" &
|
||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
|
||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
|
||||
when nimCoroutines:
|
||||
result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
for stack in items(gch.stack):
|
||||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
GC_enable()
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -373,12 +373,22 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
|
|||
## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
|
||||
## happens to ensure the GC can continue to work after the call
|
||||
## to ``deallocHeap``.
|
||||
template deallocCell(x) =
|
||||
if isCell(x):
|
||||
# cast to PCell is correct here:
|
||||
prepareDealloc(cast[PCell](x))
|
||||
|
||||
if runFinalizers:
|
||||
for x in allObjects(gch.region):
|
||||
if isCell(x):
|
||||
# cast to PCell is correct here:
|
||||
var c = cast[PCell](x)
|
||||
prepareDealloc(c)
|
||||
when not declared(allObjectsAsProc):
|
||||
for x in allObjects(gch.region):
|
||||
deallocCell(x)
|
||||
else:
|
||||
var spaceIter: ObjectSpaceIter
|
||||
while true:
|
||||
let x = allObjectsAsProc(gch.region, addr spaceIter)
|
||||
if spaceIter.state < 0: break
|
||||
deallocCell(x)
|
||||
|
||||
deallocOsPages(gch.region)
|
||||
zeroMem(addr gch.region, sizeof(gch.region))
|
||||
if allowGcAfterwards:
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
|||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
|
|
@ -264,13 +264,13 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
|||
proc forAllChildren(cell: PCell, op: WalkOp) =
|
||||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
gcAssert(cell.typ != nil, "forAllChildren: 2")
|
||||
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
|
||||
gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 3"
|
||||
let marker = cell.typ.marker
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
of tyRef, tyOptAsRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
|
|
@ -285,7 +285,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
|||
# generates a new object and sets its reference counter to 0
|
||||
incTypeSize typ, size
|
||||
acquire(gch)
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
|
|
@ -526,10 +526,10 @@ when not defined(useNimRtl):
|
|||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||
"[GC] freed objects: " & $gch.stat.freedObjects & "\n"
|
||||
when nimCoroutines:
|
||||
result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
for stack in items(gch.stack):
|
||||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,21 @@ type
|
|||
tyUInt16,
|
||||
tyUInt32,
|
||||
tyUInt64,
|
||||
tyOptAsRef, tyUnused1, tyUnused2,
|
||||
tyVarargsHidden,
|
||||
tyUnusedHidden,
|
||||
tyProxyHidden,
|
||||
tyBuiltInTypeClassHidden,
|
||||
tyUserTypeClassHidden,
|
||||
tyUserTypeClassInstHidden,
|
||||
tyCompositeTypeClassHidden,
|
||||
tyInferredHidden,
|
||||
tyAndHidden, tyOrHidden, tyNotHidden,
|
||||
tyAnythingHidden,
|
||||
tyStaticHidden,
|
||||
tyFromExprHidden,
|
||||
tyOpt,
|
||||
tyVoidHidden
|
||||
|
||||
TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
|
||||
TNimNode {.codegenType.} = object
|
||||
|
|
|
|||
|
|
@ -564,7 +564,11 @@ else:
|
|||
|
||||
when not declared(nimNewSeqOfCap):
|
||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||
result = newObj(typ, addInt(mulInt(cap, typ.base.size), GenericSeqSize))
|
||||
let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
|
||||
when declared(newObjNoInit):
|
||||
result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
|
||||
else:
|
||||
result = newObj(typ, s)
|
||||
cast[PGenericSeq](result).len = 0
|
||||
cast[PGenericSeq](result).reserved = cap
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,15 @@
|
|||
# Nim's configuration system now uses Nim for scripting. This module provides
|
||||
# a few things that are required for this to work.
|
||||
|
||||
const
|
||||
buildOS* {.magic: "BuildOS".}: string = ""
|
||||
## The OS this build is running on. Can be different from ``system.hostOS``
|
||||
## for cross compilations.
|
||||
|
||||
buildCPU* {.magic: "BuildCPU".}: string = ""
|
||||
## The CPU this build is running on. Can be different from ``system.hostCPU``
|
||||
## for cross compilations.
|
||||
|
||||
template builtin = discard
|
||||
|
||||
# We know the effects better than the compiler:
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
|
|||
if str == nil: NimString(nil)
|
||||
else: toNimStr(str, str.len)
|
||||
|
||||
template wasMoved(x: NimString): bool = false
|
||||
# (x.reserved and seqShallowFlag) != 0
|
||||
|
||||
proc copyString(src: NimString): NimString {.compilerRtl.} =
|
||||
if src != nil:
|
||||
if (src.reserved and seqShallowFlag) != 0:
|
||||
|
|
@ -103,6 +106,16 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
|
|||
result = rawNewStringNoInit(src.len)
|
||||
result.len = src.len
|
||||
copyMem(addr(result.data), addr(src.data), src.len + 1)
|
||||
sysAssert((seqShallowFlag and result.reserved) == 0, "copyString")
|
||||
when defined(nimShallowStrings):
|
||||
if (src.reserved and strlitFlag) != 0:
|
||||
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
|
||||
|
||||
proc newOwnedString(src: NimString; n: int): NimString =
|
||||
result = rawNewStringNoInit(n)
|
||||
result.len = n
|
||||
copyMem(addr(result.data), addr(src.data), n)
|
||||
result.data[n] = '\0'
|
||||
|
||||
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
||||
if src != nil:
|
||||
|
|
@ -116,6 +129,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
|||
result = rawNewStringNoInit(src.len)
|
||||
result.len = src.len
|
||||
copyMem(addr(result.data), addr(src.data), src.len + 1)
|
||||
sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
|
||||
when defined(nimShallowStrings):
|
||||
if (src.reserved and strlitFlag) != 0:
|
||||
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
|
||||
|
||||
proc copyDeepString(src: NimString): NimString {.inline.} =
|
||||
if src != nil:
|
||||
|
|
@ -140,9 +157,12 @@ proc addChar(s: NimString, c: char): NimString =
|
|||
# is compilerproc!
|
||||
result = s
|
||||
if result.len >= result.space:
|
||||
result.reserved = resize(result.space)
|
||||
let r = resize(result.space)
|
||||
result = cast[NimString](growObj(result,
|
||||
sizeof(TGenericSeq) + result.reserved + 1))
|
||||
sizeof(TGenericSeq) + r + 1))
|
||||
result.reserved = r
|
||||
elif wasMoved(s):
|
||||
result = newOwnedString(s, s.len)
|
||||
result.data[result.len] = c
|
||||
result.data[result.len+1] = '\0'
|
||||
inc(result.len)
|
||||
|
|
@ -179,7 +199,7 @@ proc addChar(s: NimString, c: char): NimString =
|
|||
# s = rawNewString(0);
|
||||
|
||||
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
|
||||
if dest.len + addlen <= dest.space:
|
||||
if dest.len + addlen <= dest.space and not wasMoved(dest):
|
||||
result = dest
|
||||
else: # slow path:
|
||||
var sp = max(resize(dest.space), dest.len + addlen)
|
||||
|
|
@ -200,7 +220,9 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
|
|||
|
||||
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
|
||||
var n = max(newLen, 0)
|
||||
if n <= s.space:
|
||||
if wasMoved(s):
|
||||
result = newOwnedString(s, n)
|
||||
elif n <= s.space:
|
||||
result = s
|
||||
else:
|
||||
result = resizeString(s, n)
|
||||
|
|
@ -218,26 +240,29 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
|||
# seq[seq->len-1] = x;
|
||||
result = seq
|
||||
if result.len >= result.space:
|
||||
result.reserved = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
|
||||
let r = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * r +
|
||||
GenericSeqSize))
|
||||
result.reserved = r
|
||||
inc(result.len)
|
||||
|
||||
proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
||||
# incrSeq version 2
|
||||
result = seq
|
||||
if result.len >= result.space:
|
||||
result.reserved = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
|
||||
let r = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * r +
|
||||
GenericSeqSize))
|
||||
result.reserved = r
|
||||
|
||||
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||
compilerRtl.} =
|
||||
result = seq
|
||||
if result.space < newLen:
|
||||
result.reserved = max(resize(result.space), newLen)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
|
||||
let r = max(resize(result.space), newLen)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * r +
|
||||
GenericSeqSize))
|
||||
result.reserved = r
|
||||
elif newLen < result.len:
|
||||
# we need to decref here, otherwise the GC leaks!
|
||||
when not defined(boehmGC) and not defined(nogc) and
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ elif defined(genode):
|
|||
proc initThread(s: var SysThread,
|
||||
stackSize: culonglong,
|
||||
entry: GenodeThreadProc,
|
||||
arg: pointer) {.
|
||||
arg: pointer,
|
||||
affinity: cuint) {.
|
||||
importcpp: "#.initThread(genodeEnv, @)".}
|
||||
|
||||
proc threadVarAlloc(): ThreadVarSlot = 0
|
||||
|
|
@ -567,6 +568,9 @@ when hostOS == "windows":
|
|||
setThreadAffinityMask(t.sys, uint(1 shl cpu))
|
||||
|
||||
elif defined(genode):
|
||||
var affinityOffset: cuint = 1
|
||||
# CPU affinity offset for next thread, safe to roll-over
|
||||
|
||||
proc createThread*[TArg](t: var Thread[TArg],
|
||||
tp: proc (arg: TArg) {.thread, nimcall.},
|
||||
param: TArg) =
|
||||
|
|
@ -577,7 +581,8 @@ elif defined(genode):
|
|||
when hasSharedHeap: t.stackSize = ThreadStackSize
|
||||
t.sys.initThread(
|
||||
ThreadStackSize.culonglong,
|
||||
threadProcWrapper[TArg], addr(t))
|
||||
threadProcWrapper[TArg], addr(t), affinityOffset)
|
||||
inc affinityOffset
|
||||
|
||||
proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
|
||||
{.hint: "cannot change Genode thread CPU affinity after initialization".}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue