mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-15 15:44:14 +00:00
merged
This commit is contained in:
@@ -395,6 +395,9 @@ type
|
||||
# sons[1]: field type
|
||||
# .n: nkDotExpr storing the field name
|
||||
|
||||
tyVoid #\
|
||||
# now different from tyEmpty, hurray!
|
||||
|
||||
static:
|
||||
# remind us when TTypeKind stops to fit in a single 64-bit word
|
||||
assert TTypeKind.high.ord <= 63
|
||||
@@ -528,8 +531,6 @@ const
|
||||
tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \
|
||||
# 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be
|
||||
# deprecated and this mess can be cleaned up.
|
||||
tfVoid* = tfVarargs # for historical reasons we conflated 'void' with
|
||||
# 'empty' ('@[]' has the type 'seq[empty]').
|
||||
tfReturnsNew* = tfInheritable
|
||||
skError* = skUnknown
|
||||
|
||||
@@ -610,7 +611,7 @@ type
|
||||
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
|
||||
mNHint, mNWarning, mNError,
|
||||
mInstantiationInfo, mGetTypeInfo, mNGenSym,
|
||||
mNimvm
|
||||
mNimvm, mIntDefine, mStrDefine
|
||||
|
||||
# things that we can evaluate safely at compile time, even if not asked for it:
|
||||
const
|
||||
@@ -1588,7 +1589,7 @@ proc isAtom*(n: PNode): bool {.inline.} =
|
||||
|
||||
proc isEmptyType*(t: PType): bool {.inline.} =
|
||||
## 'void' and 'stmt' types are often equivalent to 'nil' these days:
|
||||
result = t == nil or t.kind in {tyEmpty, tyStmt}
|
||||
result = t == nil or t.kind in {tyVoid, tyStmt}
|
||||
|
||||
proc makeStmtList*(n: PNode): PNode =
|
||||
if n.kind == nkStmtList:
|
||||
|
||||
@@ -124,7 +124,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
|
||||
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
|
||||
of tyArray, tyArrayConstr:
|
||||
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
|
||||
else:
|
||||
else:
|
||||
internalError("openArrayLoc: " & typeToString(a.t))
|
||||
else: internalError("openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
@@ -260,7 +260,11 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
else:
|
||||
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
|
||||
else:
|
||||
result = genArgNoParam(p, ri.sons[i])
|
||||
if tfVarargs notin typ.flags:
|
||||
localError(ri.info, "wrong argument count")
|
||||
result = nil
|
||||
else:
|
||||
result = genArgNoParam(p, ri.sons[i])
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
|
||||
@@ -1320,7 +1320,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
||||
putIntoDest(p, d, e.typ,
|
||||
ropecg(p.module, "#reprAny($1, $2)", [
|
||||
rdLoc(a), genTypeInfo(p.module, t)]), a.s)
|
||||
of tyEmpty:
|
||||
of tyEmpty, tyVoid:
|
||||
localError(e.info, "'repr' doesn't support 'void' type")
|
||||
else:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
|
||||
|
||||
@@ -416,7 +416,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
addf(result, "union{$n$1} $2;$n", [unionBody, uname])
|
||||
of nkSym:
|
||||
field = n.sym
|
||||
if field.typ.kind == tyEmpty: return
|
||||
if field.typ.kind == tyVoid: return
|
||||
#assert(field.ast == nil)
|
||||
sname = mangleRecFieldName(field, rectype)
|
||||
if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname]
|
||||
@@ -663,7 +663,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
|
||||
chunkStart = i
|
||||
|
||||
let typeInSlot = resolveStarsInCppType(typ, idx + 1, stars)
|
||||
if typeInSlot == nil or typeInSlot.kind == tyEmpty:
|
||||
if typeInSlot == nil or typeInSlot.kind == tyVoid:
|
||||
result.add(~"void")
|
||||
else:
|
||||
result.add getTypeDescAux(m, typeInSlot, check)
|
||||
@@ -1022,7 +1022,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
|
||||
[result, rope(typeToString(t))])
|
||||
return "(&".rope & result & ")".rope
|
||||
case t.kind
|
||||
of tyEmpty: result = rope"0"
|
||||
of tyEmpty, tyVoid: result = rope"0"
|
||||
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
|
||||
genTypeInfoAuxBase(m, t, t, result, rope"0")
|
||||
of tyProc:
|
||||
|
||||
@@ -93,7 +93,7 @@ proc getUniqueType*(key: PType): PType =
|
||||
# produced instead of ``NI``.
|
||||
result = key
|
||||
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
|
||||
tyCString, tyNone, tyBigNum:
|
||||
tyCString, tyNone, tyBigNum, tyVoid:
|
||||
result = gCanonicalTypes[k]
|
||||
if result == nil:
|
||||
gCanonicalTypes[k] = key
|
||||
|
||||
@@ -158,7 +158,7 @@ var
|
||||
enableNotes: TNoteKinds
|
||||
disableNotes: TNoteKinds
|
||||
|
||||
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
info: TLineInfo; orig: string) =
|
||||
var id = "" # arg = "X]:on|off"
|
||||
var i = 0
|
||||
@@ -342,7 +342,11 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
discard "allow for backwards compatibility, but don't do anything"
|
||||
of "define", "d":
|
||||
expectArg(switch, arg, pass, info)
|
||||
defineSymbol(arg)
|
||||
if {':', '='} in arg:
|
||||
splitSwitch(arg, key, val, pass, info)
|
||||
defineSymbol(key, val)
|
||||
else:
|
||||
defineSymbol(arg)
|
||||
of "undef", "u":
|
||||
expectArg(switch, arg, pass, info)
|
||||
undefSymbol(arg)
|
||||
|
||||
@@ -19,8 +19,8 @@ var gSymbols: StringTableRef
|
||||
const
|
||||
catNone = "false"
|
||||
|
||||
proc defineSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "true"
|
||||
proc defineSymbol*(symbol: string, value: string = "true") =
|
||||
gSymbols[symbol] = value
|
||||
|
||||
proc undefSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = catNone
|
||||
@@ -62,6 +62,11 @@ proc isDefined*(symbol: string): bool =
|
||||
|
||||
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
|
||||
|
||||
proc lookupSymbol*(symbol: string): string =
|
||||
result = if isDefined(symbol): gSymbols[symbol] else: nil
|
||||
|
||||
proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s)
|
||||
|
||||
iterator definedSymbolNames*: string =
|
||||
for key, val in pairs(gSymbols):
|
||||
if val != catNone: yield key
|
||||
|
||||
@@ -380,8 +380,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
||||
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
|
||||
of tkSpaces, tkInvalid:
|
||||
add(result, literal)
|
||||
of tkCurlyDotLe:
|
||||
dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
|
||||
"\\spanOther{$1}",
|
||||
[rope(esc(d.target, literal))])
|
||||
of tkCurlyDotRi:
|
||||
dispA(result, "</div><span class=\"Other pragmaend\">$1</span>",
|
||||
"\\spanOther{$1}",
|
||||
[rope(esc(d.target, literal))])
|
||||
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
|
||||
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
|
||||
tkBracketDotLe, tkBracketDotRi, tkParDotLe,
|
||||
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
|
||||
tkAccent, tkColonColon,
|
||||
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
|
||||
|
||||
@@ -163,7 +163,7 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||
of tyNil: result = etyNull
|
||||
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
|
||||
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
|
||||
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
|
||||
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses, tyVoid:
|
||||
result = etyNone
|
||||
of tyProc: result = etyProc
|
||||
of tyCString: result = etyString
|
||||
@@ -1215,6 +1215,7 @@ proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
|
||||
genArgNoParam(p, it, r)
|
||||
else:
|
||||
genArg(p, it, paramType.sym, r)
|
||||
inc generated
|
||||
|
||||
proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
|
||||
r: var TCompRes) =
|
||||
@@ -1557,8 +1558,16 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of tyEnum, tyOrdinal:
|
||||
gen(p, n.sons[1], r)
|
||||
useMagic(p, "cstrToNimstr")
|
||||
var offset = ""
|
||||
if t.kind == tyEnum:
|
||||
let firstFieldOffset = t.n.sons[0].sym.position
|
||||
if firstFieldOffset < 0:
|
||||
offset = "+" & $(-firstFieldOffset)
|
||||
elif firstFieldOffset > 0:
|
||||
offset = "-" & $firstFieldOffset
|
||||
|
||||
r.kind = resExpr
|
||||
r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res]
|
||||
r.res = "cstrToNimstr($1.node.sons[$2$3].name)" % [genTypeInfo(p, t), r.res, rope(offset)]
|
||||
else:
|
||||
# XXX:
|
||||
internalError(n.info, "genRepr: Not implemented")
|
||||
|
||||
@@ -184,8 +184,8 @@ proc importModule*(s: PSym, fileIdx: int32): PSym {.procvar.} =
|
||||
# this is called by the semantic checking phase
|
||||
result = compileModule(fileIdx, {})
|
||||
if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
|
||||
if sfSystemModule in result.flags:
|
||||
localError(result.info, errAttemptToRedefine, result.name.s)
|
||||
#if sfSystemModule in result.flags:
|
||||
# localError(result.info, errAttemptToRedefine, result.name.s)
|
||||
# restore the notes for outer module:
|
||||
gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
|
||||
else: ForeignPackageNotes
|
||||
|
||||
@@ -75,7 +75,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
|
||||
result = matchNodeKinds(p.constraint, n)
|
||||
if not result: return
|
||||
if isNil(n.typ):
|
||||
result = p.typ.kind in {tyEmpty, tyStmt}
|
||||
result = p.typ.kind in {tyVoid, tyStmt}
|
||||
else:
|
||||
result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ const
|
||||
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
|
||||
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
|
||||
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims}
|
||||
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
|
||||
wIntDefine, wStrDefine}
|
||||
letPragmas* = varPragmas
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
|
||||
wThread, wRaises, wLocks, wTags, wGcSafe}
|
||||
@@ -898,6 +899,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||
of wBase:
|
||||
noVal(it)
|
||||
sym.flags.incl sfBase
|
||||
of wIntDefine:
|
||||
sym.magic = mIntDefine
|
||||
of wStrDefine:
|
||||
sym.magic = mStrDefine
|
||||
else: invalidPragma(it)
|
||||
else: invalidPragma(it)
|
||||
|
||||
|
||||
@@ -704,7 +704,10 @@ proc gcase(g: var TSrcGen, n: PNode) =
|
||||
proc gproc(g: var TSrcGen, n: PNode) =
|
||||
var c: TContext
|
||||
if n.sons[namePos].kind == nkSym:
|
||||
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
|
||||
let s = n.sons[namePos].sym
|
||||
put(g, tkSymbol, renderDefinitionName(s))
|
||||
if sfGenSym in s.flags:
|
||||
put(g, tkIntLit, $s.id)
|
||||
else:
|
||||
gsub(g, n.sons[namePos])
|
||||
|
||||
@@ -798,7 +801,8 @@ proc gident(g: var TSrcGen, n: PNode) =
|
||||
else:
|
||||
t = tkOpr
|
||||
put(g, t, s)
|
||||
if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id)
|
||||
if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
|
||||
put(g, tkIntLit, $n.sym.id)
|
||||
|
||||
proc doParamsAux(g: var TSrcGen, params: PNode) =
|
||||
if params.len > 1:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import
|
||||
ast, modules, passes, passaux, condsyms,
|
||||
options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
|
||||
os, times, osproc
|
||||
os, times, osproc, wordrecg
|
||||
|
||||
# we support 'cmpIgnoreStyle' natively for efficiency:
|
||||
from strutils import cmpIgnoreStyle
|
||||
@@ -116,7 +116,12 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext =
|
||||
setResult(a, options.command)
|
||||
cbconf switch:
|
||||
processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
|
||||
|
||||
cbconf hintImpl:
|
||||
processSpecificNote(a.getString 0, wHint, passPP, unknownLineInfo(),
|
||||
a.getString 1)
|
||||
cbconf warningImpl:
|
||||
processSpecificNote(a.getString 0, wWarning, passPP, unknownLineInfo(),
|
||||
a.getString 1)
|
||||
|
||||
proc runNimScript*(scriptName: string; freshDefines=true) =
|
||||
passes.gIncludeFile = includeModule
|
||||
|
||||
@@ -307,6 +307,13 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||
|
||||
include hlo, seminst, semcall
|
||||
|
||||
when false:
|
||||
# hopefully not required:
|
||||
proc resetSemFlag(n: PNode) =
|
||||
excl n.flags, nfSem
|
||||
for i in 0 ..< n.safeLen:
|
||||
resetSemFlag(n[i])
|
||||
|
||||
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
|
||||
flags: TExprFlags): PNode =
|
||||
## Semantically check the output of a macro.
|
||||
@@ -320,6 +327,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
|
||||
c.friendModules.add(s.owner.getModule)
|
||||
|
||||
result = n
|
||||
excl(n.flags, nfSem)
|
||||
#resetSemFlag n
|
||||
if s.typ.sons[0] == nil:
|
||||
result = semStmt(c, result)
|
||||
else:
|
||||
@@ -409,9 +418,6 @@ proc myOpen(module: PSym): PPassContext =
|
||||
c.importTable.addSym(module) # a module knows itself
|
||||
if sfSystemModule in module.flags:
|
||||
magicsys.systemModule = module # set global variable!
|
||||
else:
|
||||
c.importTable.addSym magicsys.systemModule # import the "System" identifier
|
||||
importAllSymbols(c, magicsys.systemModule)
|
||||
c.topLevelScope = openScope(c)
|
||||
# don't be verbose unless the module belongs to the main package:
|
||||
if module.owner.id == gMainPackageId:
|
||||
@@ -425,7 +431,29 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
|
||||
result = myOpen(module)
|
||||
for m in items(rd.methods): methodDef(m, true)
|
||||
|
||||
proc isImportSystemStmt(n: PNode): bool =
|
||||
if magicsys.systemModule == nil: return false
|
||||
case n.kind
|
||||
of nkImportStmt:
|
||||
for x in n:
|
||||
let f = checkModuleName(x)
|
||||
if f == magicsys.systemModule.info.fileIndex:
|
||||
return true
|
||||
of nkImportExceptStmt, nkFromStmt:
|
||||
let f = checkModuleName(n[0])
|
||||
if f == magicsys.systemModule.info.fileIndex:
|
||||
return true
|
||||
else: discard
|
||||
|
||||
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||
if c.topStmts == 0 and not isImportSystemStmt(n):
|
||||
if sfSystemModule notin c.module.flags and
|
||||
n.kind notin {nkEmpty, nkCommentStmt}:
|
||||
c.importTable.addSym magicsys.systemModule # import the "System" identifier
|
||||
importAllSymbols(c, magicsys.systemModule)
|
||||
inc c.topStmts
|
||||
else:
|
||||
inc c.topStmts
|
||||
if sfNoForward in c.module.flags:
|
||||
result = semAllTypeSections(c, n)
|
||||
else:
|
||||
|
||||
@@ -183,7 +183,7 @@ proc newSeqCall(c: PContext; x, y: PNode): PNode =
|
||||
|
||||
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
of tyNone, tyEmpty: discard
|
||||
of tyNone, tyEmpty, tyVoid: discard
|
||||
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
|
||||
tyPtr, tyString, tyRef:
|
||||
defaultOp(c, t, body, x, y)
|
||||
|
||||
@@ -99,6 +99,7 @@ type
|
||||
unknownIdents*: IntSet # ids of all unknown identifiers to prevent
|
||||
# naming it multiple times
|
||||
generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
|
||||
topStmts*: int # counts the number of encountered top level statements
|
||||
lastGenericIdx*: int # used for the generics stack
|
||||
hloLoopDetector*: int # used to prevent endless loops in the HLO
|
||||
inParallelStmt*: int
|
||||
|
||||
@@ -32,8 +32,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyVar: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyEmpty, c)
|
||||
result.typ.flags.incl tfVoid
|
||||
result.typ = newTypeS(tyVoid, c)
|
||||
else:
|
||||
localError(n.info, errExprXHasNoType,
|
||||
renderTree(result, {renderNoComments}))
|
||||
@@ -601,6 +600,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return
|
||||
var callee = n.sons[0].sym
|
||||
# workaround for bug #537 (overly aggressive inlining leading to
|
||||
# wrong NimNode semantics):
|
||||
if n.typ != nil and tfTriggersCompileTime in n.typ.flags: return
|
||||
|
||||
# constant folding that is necessary for correctness of semantic pass:
|
||||
if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil:
|
||||
|
||||
@@ -640,6 +640,12 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
of mNaN: result = newFloatNodeT(NaN, n)
|
||||
of mInf: result = newFloatNodeT(Inf, n)
|
||||
of mNegInf: result = newFloatNodeT(NegInf, n)
|
||||
of mIntDefine:
|
||||
if isDefined(s.name):
|
||||
result = newIntNodeT(lookupSymbol(s.name).parseInt, n)
|
||||
of mStrDefine:
|
||||
if isDefined(s.name):
|
||||
result = newStrNodeT(lookupSymbol(s.name), n)
|
||||
else:
|
||||
if sfFakeConst notin s.flags: result = copyTree(s.ast)
|
||||
of {skProc, skMethod}:
|
||||
|
||||
@@ -426,7 +426,12 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
|
||||
n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx)
|
||||
var body: PNode
|
||||
if n.sons[namePos].kind == nkSym: body = n.sons[namePos].sym.getBody
|
||||
if n.sons[namePos].kind == nkSym:
|
||||
let s = n.sons[namePos].sym
|
||||
if sfGenSym in s.flags and s.ast == nil:
|
||||
body = n.sons[bodyPos]
|
||||
else:
|
||||
body = s.getBody
|
||||
else: body = n.sons[bodyPos]
|
||||
n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
|
||||
closeScope(c)
|
||||
|
||||
@@ -205,7 +205,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
# The solution would be to move this logic into semtypinst, but
|
||||
# at this point semtypinst have to become part of sem, because it
|
||||
# will need to use openScope, addDecl, etc.
|
||||
addDecl(c, prc)
|
||||
#addDecl(c, prc)
|
||||
|
||||
pushInfoContext(info)
|
||||
var cl = initTypeVars(c, pt, info, nil)
|
||||
|
||||
@@ -138,7 +138,7 @@ proc fixNilType(n: PNode) =
|
||||
|
||||
proc discardCheck(c: PContext, result: PNode) =
|
||||
if c.inTypeClass > 0: return
|
||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
|
||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
||||
if result.kind == nkNilLit:
|
||||
result.typ = nil
|
||||
message(result.info, warnNilStatement)
|
||||
@@ -711,7 +711,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
a.sons[1] = s.typ.n
|
||||
s.typ.size = -1 # could not be computed properly
|
||||
# we fill it out later. For magic generics like 'seq', it won't be filled
|
||||
# so we use tyEmpty instead of nil to not crash for strange conversions
|
||||
# so we use tyNone instead of nil to not crash for strange conversions
|
||||
# like: mydata.seq
|
||||
rawAddSon(s.typ, newTypeS(tyNone, c))
|
||||
s.ast = a
|
||||
|
||||
@@ -945,7 +945,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if isType: localError(a.info, "':' expected")
|
||||
if kind in {skTemplate, skMacro}:
|
||||
typ = newTypeS(tyExpr, c)
|
||||
elif skipTypes(typ, {tyGenericInst}).kind == tyEmpty:
|
||||
elif skipTypes(typ, {tyGenericInst}).kind == tyVoid:
|
||||
continue
|
||||
for j in countup(0, length-3):
|
||||
var arg = newSymG(skParam, a.sons[j], c)
|
||||
@@ -977,7 +977,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if r != nil:
|
||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||
# compiler only checks for 'nil':
|
||||
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
|
||||
if skipTypes(r, {tyGenericInst}).kind != tyVoid:
|
||||
# 'auto' as a return type does not imply a generic:
|
||||
if r.kind == tyAnything:
|
||||
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
|
||||
@@ -1390,10 +1390,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
setMagicType(m, tyTypeDesc, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mVoidType:
|
||||
setMagicType(m, tyEmpty, 0)
|
||||
# for historical reasons we conflate 'void' with 'empty' so that '@[]'
|
||||
# has the type 'seq[void]'.
|
||||
m.typ.flags.incl tfVoid
|
||||
setMagicType(m, tyVoid, 0)
|
||||
of mArray:
|
||||
setMagicType(m, tyArray, 0)
|
||||
of mOpenArray:
|
||||
|
||||
@@ -162,7 +162,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
discard
|
||||
of nkSym:
|
||||
result.sym = replaceTypeVarsS(cl, n.sym)
|
||||
if result.sym.typ.kind == tyEmpty:
|
||||
if result.sym.typ.kind == tyVoid:
|
||||
# don't add the 'void' field
|
||||
result = newNode(nkRecList, n.info)
|
||||
of nkRecWhen:
|
||||
@@ -316,15 +316,15 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
proc eraseVoidParams*(t: PType) =
|
||||
# transform '(): void' into '()' because old parts of the compiler really
|
||||
# don't deal with '(): void':
|
||||
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
||||
if t.sons[0] != nil and t.sons[0].kind == tyVoid:
|
||||
t.sons[0] = nil
|
||||
|
||||
for i in 1 .. <t.sonsLen:
|
||||
# don't touch any memory unless necessary
|
||||
if t.sons[i].kind == tyEmpty:
|
||||
if t.sons[i].kind == tyVoid:
|
||||
var pos = i
|
||||
for j in i+1 .. <t.sonsLen:
|
||||
if t.sons[j].kind != tyEmpty:
|
||||
if t.sons[j].kind != tyVoid:
|
||||
t.sons[pos] = t.sons[j]
|
||||
t.n.sons[pos] = t.n.sons[j]
|
||||
inc pos
|
||||
|
||||
@@ -949,8 +949,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
result = isConvertible
|
||||
else: discard
|
||||
|
||||
of tyEmpty:
|
||||
if a.kind == tyEmpty: result = isEqual
|
||||
of tyEmpty, tyVoid:
|
||||
if a.kind == f.kind: result = isEqual
|
||||
|
||||
of tyGenericInst:
|
||||
result = typeRel(c, lastSon(f), a)
|
||||
|
||||
@@ -407,7 +407,8 @@ const
|
||||
"!", "varargs[$1]", "iter[$1]", "Error Type",
|
||||
"BuiltInTypeClass", "UserTypeClass",
|
||||
"UserTypeClassInst", "CompositeTypeClass",
|
||||
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
|
||||
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
|
||||
"void"]
|
||||
|
||||
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
|
||||
|
||||
@@ -727,6 +728,7 @@ proc equalParam(a, b: PSym): TParamsEquality =
|
||||
result = paramsNotEqual
|
||||
|
||||
proc sameConstraints(a, b: PNode): bool =
|
||||
if isNil(a) and isNil(b): return true
|
||||
internalAssert a.len == b.len
|
||||
for i in 1 .. <a.len:
|
||||
if not exprStructuralEquivalent(a[i].sym.constraint,
|
||||
@@ -922,7 +924,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
|
||||
case a.kind
|
||||
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
||||
tyInt..tyBigNum, tyStmt, tyExpr:
|
||||
tyInt..tyBigNum, tyStmt, tyExpr, tyVoid:
|
||||
result = sameFlags(a, b)
|
||||
of tyStatic, tyFromExpr:
|
||||
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
|
||||
@@ -1108,7 +1110,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
result = nil
|
||||
of tyExpr, tyStmt, tyStatic:
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
of tyEmpty:
|
||||
of tyVoid:
|
||||
if taField notin flags: result = t
|
||||
of tyTypeClasses:
|
||||
if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
|
||||
@@ -1153,7 +1155,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
if result != nil: break
|
||||
if result.isNil and t.n != nil:
|
||||
result = typeAllowedNode(marker, t.n, kind, flags)
|
||||
of tyProxy:
|
||||
of tyProxy, tyEmpty:
|
||||
# for now same as error node; we say it's a valid type as it should
|
||||
# prevent cascading errors:
|
||||
result = nil
|
||||
|
||||
@@ -80,7 +80,7 @@ proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo;
|
||||
for i in 0 .. < t.len:
|
||||
if t.sons[i] == nil:
|
||||
let void = atomicTypeX("void", mVoid, t, info)
|
||||
void.typ = newType(tyEmpty, t.owner)
|
||||
void.typ = newType(tyVoid, t.owner)
|
||||
result.add void
|
||||
else:
|
||||
result.add mapTypeToAstX(t.sons[i], info, inst)
|
||||
@@ -124,7 +124,8 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||
of tyNil: result = atomicType("nil", mNil)
|
||||
of tyExpr: result = atomicType("expr", mExpr)
|
||||
of tyStmt: result = atomicType("stmt", mStmt)
|
||||
of tyEmpty: result = atomicType("void", mVoid)
|
||||
of tyVoid: result = atomicType("void", mVoid)
|
||||
of tyEmpty: result = atomicType("empty", mNone)
|
||||
of tyArrayConstr, tyArray:
|
||||
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||
result.add atomicType("array", mArray)
|
||||
|
||||
@@ -267,7 +267,7 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
|
||||
proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
|
||||
# stmt is different from 'void' in meta programming contexts.
|
||||
# So we only set dest to -1 if 'void':
|
||||
if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty):
|
||||
if dest >= 0 and (n.typ.isNil or n.typ.kind == tyVoid):
|
||||
c.freeTemp(dest)
|
||||
dest = -1
|
||||
|
||||
@@ -374,7 +374,7 @@ proc canonValue*(n: PNode): PNode =
|
||||
|
||||
proc rawGenLiteral(c: PCtx; n: PNode): int =
|
||||
result = c.constants.len
|
||||
assert(n.kind != nkCall)
|
||||
#assert(n.kind != nkCall)
|
||||
n.flags.incl nfAllConst
|
||||
c.constants.add n.canonValue
|
||||
internalAssert result < 0x7fff
|
||||
@@ -497,12 +497,30 @@ proc genReturn(c: PCtx; n: PNode) =
|
||||
gen(c, n.sons[0])
|
||||
c.gABC(n, opcRet)
|
||||
|
||||
|
||||
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
|
||||
# opcLdConst is now always valid. We produce the necessary copy in the
|
||||
# assignments now:
|
||||
#var opc = opcLdConst
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
#elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
|
||||
let lit = genLiteral(c, n)
|
||||
c.gABx(n, opcLdConst, dest, lit)
|
||||
|
||||
proc genCall(c: PCtx; n: PNode; dest: var TDest) =
|
||||
# it can happen that due to inlining we have a 'n' that should be
|
||||
# treated as a constant (see issue #537).
|
||||
#if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode:
|
||||
# genLit(c, n, dest)
|
||||
# return
|
||||
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
|
||||
let x = c.getTempRange(n.len, slotTempUnknown)
|
||||
# varargs need 'opcSetType' for the FFI support:
|
||||
let fntyp = n.sons[0].typ
|
||||
let fntyp = skipTypes(n.sons[0].typ, abstractInst)
|
||||
for i in 0.. <n.len:
|
||||
#if i > 0 and i < sonsLen(fntyp):
|
||||
# let paramType = fntyp.n.sons[i]
|
||||
# if paramType.typ.isCompileTimeOnly: continue
|
||||
var r: TRegister = x+i
|
||||
c.gen(n.sons[i], r)
|
||||
if i >= fntyp.len:
|
||||
@@ -1307,15 +1325,6 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
let dest = c.genx(le, {gfAddrOf})
|
||||
genAsgn(c, dest, ri, requiresCopy)
|
||||
|
||||
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
|
||||
# opcLdConst is now always valid. We produce the necessary copy in the
|
||||
# assignments now:
|
||||
#var opc = opcLdConst
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
#elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
|
||||
let lit = genLiteral(c, n)
|
||||
c.gABx(n, opcLdConst, dest, lit)
|
||||
|
||||
proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
|
||||
var n = newNode(nkType)
|
||||
n.typ = t
|
||||
@@ -1788,6 +1797,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
genConv(c, n, n.sons[1], dest, opcCast)
|
||||
else:
|
||||
globalError(n.info, errGenerated, "VM is not allowed to 'cast'")
|
||||
of nkTypeOfExpr:
|
||||
genTypeLit(c, n.typ, dest)
|
||||
else:
|
||||
globalError(n.info, errGenerated, "cannot generate VM code for " & $n)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ type
|
||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||
wStar, wMinus,
|
||||
wMagic, wThread, wFinal, wProfiler, wObjChecks,
|
||||
wIntDefine, wStrDefine,
|
||||
|
||||
wDestroy,
|
||||
|
||||
@@ -121,7 +122,7 @@ const
|
||||
|
||||
":", "::", "=", ".", "..",
|
||||
"*", "-",
|
||||
"magic", "thread", "final", "profiler", "objchecks",
|
||||
"magic", "thread", "final", "profiler", "objchecks", "intdefine", "strdefine",
|
||||
|
||||
"destroy",
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# This is the config file for the documentation generator.
|
||||
# (c) 2012 Andreas Rumpf
|
||||
# (c) 2016 Andreas Rumpf
|
||||
# Feel free to edit the templates as you need. If you modify this file, it
|
||||
# might be worth updating the hardcoded values in packages/docutils/rstgen.nim
|
||||
|
||||
@@ -1235,10 +1235,46 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA==");
|
||||
margin-bottom: -5px; }
|
||||
div.pragma {
|
||||
display: none;
|
||||
}
|
||||
span.pragmabegin {
|
||||
cursor: pointer;
|
||||
}
|
||||
span.pragmaend {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function togglepragma(d) {
|
||||
if (d.style.display != 'inline')
|
||||
d.style.display = 'inline';
|
||||
else
|
||||
d.style.display = 'none';
|
||||
}
|
||||
|
||||
function main() {
|
||||
var elements = document.getElementsByClassName("pragmabegin");
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
var e = elements[i];
|
||||
e.onclick = function(event) {
|
||||
togglepragma(event.target.nextSibling);
|
||||
};
|
||||
}
|
||||
var elements = document.getElementsByClassName("pragmaend");
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
var e = elements[i];
|
||||
e.onclick = function(event) {
|
||||
togglepragma(event.target.previousSibling);
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<body onload="main()">
|
||||
<div class="document" id="documentId">
|
||||
<div class="container">
|
||||
<h1 class="title">$title</h1>
|
||||
|
||||
@@ -11,7 +11,9 @@ Arguments:
|
||||
arguments are passed to the program being run (if --run option is selected)
|
||||
Options:
|
||||
-p, --path:PATH add path to search paths
|
||||
-d, --define:SYMBOL define a conditional symbol
|
||||
-d, --define:SYMBOL(:VAL)
|
||||
define a conditional symbol
|
||||
(Optionally: Define the value for that symbol)
|
||||
-u, --undef:SYMBOL undefine a conditional symbol
|
||||
-f, --forceBuild force rebuilding of all modules
|
||||
--stackTrace:on|off turn stack tracing on|off
|
||||
|
||||
@@ -1011,3 +1011,30 @@ debugging:
|
||||
|
||||
# ... complex code here that produces crashes ...
|
||||
|
||||
compile time define pragmas
|
||||
---------------------------
|
||||
|
||||
The pragmas listed here can be used to optionally accept values from
|
||||
the -d/--define option at compile time.
|
||||
|
||||
The implementation currently provides the following possible options (various
|
||||
others may be added later).
|
||||
|
||||
=============== ============================================
|
||||
pragma description
|
||||
=============== ============================================
|
||||
intdefine Reads in a build-time define as an integer
|
||||
strdefine Reads in a build-time define as a string
|
||||
=============== ============================================
|
||||
|
||||
.. code-block:: nim
|
||||
const FooBar {.intdefine.}: int = 5
|
||||
echo FooBar
|
||||
|
||||
.. code-block:: bash
|
||||
nim c -d:FooBar=42 foobar.c
|
||||
|
||||
In the above example, providing the -d flag causes the symbol
|
||||
``FooBar`` to be overwritten at compile time, printing out 42. If the
|
||||
``-d:FooBar=42`` were to be omitted, the default value of 5 would be
|
||||
used.
|
||||
|
||||
@@ -215,6 +215,12 @@ the closure and its enclosing scope (i.e. any modifications made to them are
|
||||
visible in both places). The closure environment may be allocated on the heap
|
||||
or on the stack if the compiler determines that this would be safe.
|
||||
|
||||
Creating closures in loops
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Since closures capture local variables by reference it is often not wanted
|
||||
behavior inside loop bodies. See `closureScope <system.html#closureScope>`_
|
||||
for details on how to change this behavior.
|
||||
|
||||
Anonymous Procs
|
||||
---------------
|
||||
@@ -223,7 +229,7 @@ Procs can also be treated as expressions, in which case it's allowed to omit
|
||||
the proc's name.
|
||||
|
||||
.. code-block:: nim
|
||||
var cities = @["Frankfurt", "Tokyo", "New York"]
|
||||
var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"]
|
||||
|
||||
cities.sort(proc (x,y: string): int =
|
||||
cmp(x.len, y.len))
|
||||
|
||||
10
doc/nimc.rst
10
doc/nimc.rst
@@ -98,6 +98,11 @@ enable builds in release mode (``-d:release``) where certain safety checks are
|
||||
omitted for better performance. Another common use is the ``-d:ssl`` switch to
|
||||
activate `SSL sockets <sockets.html>`_.
|
||||
|
||||
Additionally, you may pass a value along with the symbol: ``-d:x=y``
|
||||
which may be used in conjunction with the `compile time define
|
||||
pragmas<manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_
|
||||
to override symbols during build time.
|
||||
|
||||
|
||||
Configuration files
|
||||
-------------------
|
||||
@@ -370,7 +375,10 @@ For example, to generate code for an `AVR`:idx: processor use this command::
|
||||
For the ``standalone`` target one needs to provide
|
||||
a file ``panicoverride.nim``.
|
||||
See ``tests/manyloc/standalone/panicoverride.nim`` for an example
|
||||
implementation.
|
||||
implementation. Additionally, users should specify the
|
||||
amount of heap space to use with the ``-d:StandaloneHeapSize=<size>``
|
||||
command line switch. Note that the total heap size will be
|
||||
``<size> * sizeof(float64)``.
|
||||
|
||||
|
||||
Nim for realtime systems
|
||||
|
||||
@@ -102,7 +102,7 @@ __clang__
|
||||
defined __ICL || \
|
||||
defined __DMC__ || \
|
||||
defined __BORLANDC__ )
|
||||
# define NIM_THREADVAR __declspec(thread)
|
||||
# define NIM_THREADVAR __declspec(thread)
|
||||
/* note that ICC (linux) and Clang are covered by __GNUC__ */
|
||||
#elif defined __GNUC__ || \
|
||||
defined __SUNPRO_C || \
|
||||
@@ -449,8 +449,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
|
||||
|
||||
/* Test to see if Nim and the C compiler agree on the size of a pointer.
|
||||
On disagreement, your C compiler will say something like:
|
||||
"error: 'assert_numbits' declared as an array with a negative size" */
|
||||
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
|
||||
"error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */
|
||||
typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1699,8 +1699,10 @@ proc processBody(node, retFutureSym: NimNode,
|
||||
else:
|
||||
result.add newCall(newIdentNode("complete"), retFutureSym)
|
||||
else:
|
||||
result.add newCall(newIdentNode("complete"), retFutureSym,
|
||||
node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt))
|
||||
let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)
|
||||
if x.kind == nnkYieldStmt: result.add x
|
||||
else:
|
||||
result.add newCall(newIdentNode("complete"), retFutureSym, x)
|
||||
|
||||
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
|
||||
return # Don't process the children of this return stmt
|
||||
@@ -1896,7 +1898,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
||||
|
||||
# -> createCb(retFuture)
|
||||
#var cbName = newIdentNode("cb")
|
||||
var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym,
|
||||
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
|
||||
newStrLitNode(prc[0].getName))
|
||||
outerProcBody.add procCb
|
||||
|
||||
@@ -1931,7 +1933,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
result.add asyncSingleProc(oneProc)
|
||||
else:
|
||||
result = asyncSingleProc(prc)
|
||||
when defined(nimAsyncDebug):
|
||||
when defined(nimDumpAsync):
|
||||
echo repr result
|
||||
|
||||
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
|
||||
|
||||
@@ -58,7 +58,7 @@ proc isValid*[A](s: HashSet[A]): bool =
|
||||
## initialized. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## proc savePreferences(options: Set[string]) =
|
||||
## proc savePreferences(options: HashSet[string]) =
|
||||
## assert options.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
@@ -72,7 +72,7 @@ proc len*[A](s: HashSet[A]): int =
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: Set[int]
|
||||
## var values: HashSet[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
@@ -338,7 +338,7 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
|
||||
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: Set[int]
|
||||
## var a: HashSet[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
|
||||
@@ -433,7 +433,7 @@ proc htmlTag*(n: XmlNode): HtmlTag =
|
||||
proc htmlTag*(s: string): HtmlTag =
|
||||
## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
|
||||
## returned.
|
||||
let s = if allLower(s): s else: s.toLower
|
||||
let s = if allLower(s): s else: toLowerAscii(s)
|
||||
result = toHtmlTag(s)
|
||||
|
||||
proc entityToUtf8*(entity: string): string =
|
||||
@@ -513,13 +513,13 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
errors.add(errorMsg(x))
|
||||
next(x)
|
||||
of xmlElementStart:
|
||||
result = newElement(x.elemName.toLower)
|
||||
result = newElement(toLowerAscii(x.elemName))
|
||||
next(x)
|
||||
untilElementEnd(x, result, errors)
|
||||
of xmlElementEnd:
|
||||
errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName))
|
||||
of xmlElementOpen:
|
||||
result = newElement(x.elemName.toLower)
|
||||
result = newElement(toLowerAscii(x.elemName))
|
||||
next(x)
|
||||
result.attrs = newStringTable()
|
||||
while true:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
461
lib/pure/ioselects/ioselectors_epoll.nim
Normal file
461
lib/pure/ioselects/ioselectors_epoll.nim
Normal file
@@ -0,0 +1,461 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2016 Eugene Kabanov
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# This module implements Linux epoll().
|
||||
|
||||
import posix, times
|
||||
|
||||
# Maximum number of events that can be returned
|
||||
const MAX_EPOLL_RESULT_EVENTS = 64
|
||||
|
||||
type
|
||||
SignalFdInfo* {.importc: "struct signalfd_siginfo",
|
||||
header: "<sys/signalfd.h>", pure, final.} = object
|
||||
ssi_signo*: uint32
|
||||
ssi_errno*: int32
|
||||
ssi_code*: int32
|
||||
ssi_pid*: uint32
|
||||
ssi_uid*: uint32
|
||||
ssi_fd*: int32
|
||||
ssi_tid*: uint32
|
||||
ssi_band*: uint32
|
||||
ssi_overrun*: uint32
|
||||
ssi_trapno*: uint32
|
||||
ssi_status*: int32
|
||||
ssi_int*: int32
|
||||
ssi_ptr*: uint64
|
||||
ssi_utime*: uint64
|
||||
ssi_stime*: uint64
|
||||
ssi_addr*: uint64
|
||||
pad* {.importc: "__pad".}: array[0..47, uint8]
|
||||
|
||||
eventFdData {.importc: "eventfd_t",
|
||||
header: "<sys/eventfd.h>", pure, final.} = uint64
|
||||
epoll_data {.importc: "union epoll_data", header: "<sys/epoll.h>",
|
||||
pure, final.} = object
|
||||
u64 {.importc: "u64".}: uint64
|
||||
epoll_event {.importc: "struct epoll_event",
|
||||
header: "<sys/epoll.h>", pure, final.} = object
|
||||
events: uint32 # Epoll events
|
||||
data: epoll_data # User data variable
|
||||
|
||||
const
|
||||
EPOLL_CTL_ADD = 1 # Add a file descriptor to the interface.
|
||||
EPOLL_CTL_DEL = 2 # Remove a file descriptor from the interface.
|
||||
EPOLL_CTL_MOD = 3 # Change file descriptor epoll_event structure.
|
||||
EPOLLIN = 0x00000001
|
||||
EPOLLOUT = 0x00000004
|
||||
EPOLLERR = 0x00000008
|
||||
EPOLLHUP = 0x00000010
|
||||
EPOLLRDHUP = 0x00002000
|
||||
EPOLLONESHOT = 1 shl 30
|
||||
|
||||
proc epoll_create(size: cint): cint
|
||||
{.importc: "epoll_create", header: "<sys/epoll.h>".}
|
||||
proc epoll_ctl(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint
|
||||
{.importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
||||
proc epoll_wait(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
timeout: cint): cint
|
||||
{.importc: "epoll_wait", header: "<sys/epoll.h>".}
|
||||
proc timerfd_create(clock_id: ClockId, flags: cint): cint
|
||||
{.cdecl, importc: "timerfd_create", header: "<sys/timerfd.h>".}
|
||||
proc timerfd_settime(ufd: cint, flags: cint,
|
||||
utmr: var Itimerspec, otmr: var Itimerspec): cint
|
||||
{.cdecl, importc: "timerfd_settime", header: "<sys/timerfd.h>".}
|
||||
proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
|
||||
{.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
|
||||
proc eventfd(count: cuint, flags: cint): cint
|
||||
{.cdecl, importc: "eventfd", header: "<sys/eventfd.h>".}
|
||||
proc ulimit(cmd: cint): clong
|
||||
{.importc: "ulimit", header: "<ulimit.h>", varargs.}
|
||||
|
||||
when hasThreadSupport:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
epollFD : cint
|
||||
maxFD : int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
count: int
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
epollFD : cint
|
||||
maxFD : int
|
||||
fds: seq[SelectorKey[T]]
|
||||
count: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
type
|
||||
SelectEventImpl = object
|
||||
efd: cint
|
||||
SelectEvent* = ptr SelectEventImpl
|
||||
|
||||
proc newSelector*[T](): Selector[T] =
|
||||
var maxFD = int(ulimit(4, 0))
|
||||
doAssert(maxFD > 0)
|
||||
|
||||
var epollFD = epoll_create(MAX_EPOLL_RESULT_EVENTS)
|
||||
if epollFD < 0:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
when hasThreadSupport:
|
||||
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
|
||||
result.epollFD = epollFD
|
||||
result.maxFD = maxFD
|
||||
result.fds = allocSharedArray[SelectorKey[T]](maxFD)
|
||||
else:
|
||||
result = Selector[T]()
|
||||
result.epollFD = epollFD
|
||||
result.maxFD = maxFD
|
||||
result.fds = newSeq[SelectorKey[T]](maxFD)
|
||||
|
||||
proc close*[T](s: Selector[T]) =
|
||||
if posix.close(s.epollFD) != 0:
|
||||
raiseOSError(osLastError())
|
||||
when hasThreadSupport:
|
||||
deallocSharedArray(s.fds)
|
||||
deallocShared(cast[pointer](s))
|
||||
|
||||
proc newSelectEvent*(): SelectEvent =
|
||||
let fdci = eventfd(0, 0)
|
||||
if fdci == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fdci)
|
||||
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
|
||||
result.efd = fdci
|
||||
|
||||
proc setEvent*(ev: SelectEvent) =
|
||||
var data : uint64 = 1
|
||||
if posix.write(ev.efd, addr data, sizeof(uint64)) == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(ev: SelectEvent) =
|
||||
discard posix.close(ev.efd)
|
||||
deallocShared(cast[pointer](ev))
|
||||
|
||||
template checkFd(s, f) =
|
||||
if f >= s.maxFD:
|
||||
raise newException(ValueError, "Maximum file descriptors exceeded")
|
||||
|
||||
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event], data: T) =
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
s.setKey(fdi, fdi, events, 0, data)
|
||||
if events != {}:
|
||||
var epv = epoll_event(events: EPOLLRDHUP)
|
||||
epv.data.u64 = fdi.uint
|
||||
if Event.Read in events: epv.events = epv.events or EPOLLIN
|
||||
if Event.Write in events: epv.events = epv.events or EPOLLOUT
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
inc(s.count)
|
||||
|
||||
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) =
|
||||
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
|
||||
Event.User, Event.Oneshot, Event.Error}
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
doAssert(pkey.events * maskEvents == {})
|
||||
if pkey.events != events:
|
||||
var epv = epoll_event(events: EPOLLRDHUP)
|
||||
epv.data.u64 = fdi.uint
|
||||
|
||||
if Event.Read in events: epv.events = epv.events or EPOLLIN
|
||||
if Event.Write in events: epv.events = epv.events or EPOLLOUT
|
||||
|
||||
if pkey.events == {}:
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
inc(s.count)
|
||||
else:
|
||||
if events != {}:
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
dec(s.count)
|
||||
pkey.events = events
|
||||
|
||||
proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
|
||||
if pkey.events != {}:
|
||||
if pkey.events * {Event.Read, Event.Write} != {}:
|
||||
var epv = epoll_event()
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
dec(s.count)
|
||||
elif Event.Timer in pkey.events:
|
||||
var epv = epoll_event()
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
discard posix.close(fdi.cint)
|
||||
dec(s.count)
|
||||
elif Event.Signal in pkey.events:
|
||||
var epv = epoll_event()
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
var nmask, omask: Sigset
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, cint(s.fds[fdi].param))
|
||||
unblockSignals(nmask, omask)
|
||||
discard posix.close(fdi.cint)
|
||||
dec(s.count)
|
||||
elif Event.Process in pkey.events:
|
||||
var epv = epoll_event()
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
var nmask, omask: Sigset
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, SIGCHLD)
|
||||
unblockSignals(nmask, omask)
|
||||
discard posix.close(fdi.cint)
|
||||
dec(s.count)
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
|
||||
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
|
||||
let fdi = int(ev.efd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
doAssert(Event.User in pkey.events)
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
var epv = epoll_event()
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
dec(s.count)
|
||||
|
||||
proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
|
||||
data: T): int {.discardable.} =
|
||||
var
|
||||
new_ts: Itimerspec
|
||||
old_ts: Itimerspec
|
||||
let fdi = timerfd_create(CLOCK_MONOTONIC, 0).int
|
||||
if fdi == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fdi.cint)
|
||||
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
|
||||
var events = {Event.Timer}
|
||||
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
||||
epv.data.u64 = fdi.uint
|
||||
if oneshot:
|
||||
new_ts.it_interval.tv_sec = 0.Time
|
||||
new_ts.it_interval.tv_nsec = 0
|
||||
new_ts.it_value.tv_sec = (timeout div 1_000).Time
|
||||
new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
|
||||
incl(events, Event.Oneshot)
|
||||
epv.events = epv.events or EPOLLONESHOT
|
||||
else:
|
||||
new_ts.it_interval.tv_sec = (timeout div 1000).Time
|
||||
new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
|
||||
new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec
|
||||
new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec
|
||||
|
||||
if timerfd_settime(fdi.cint, cint(0), new_ts, old_ts) == -1:
|
||||
raiseOSError(osLastError())
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
s.setKey(fdi, fdi, events, 0, data)
|
||||
inc(s.count)
|
||||
result = fdi
|
||||
|
||||
proc registerSignal*[T](s: Selector[T], signal: int,
|
||||
data: T): int {.discardable.} =
|
||||
var
|
||||
nmask: Sigset
|
||||
omask: Sigset
|
||||
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, cint(signal))
|
||||
blockSignals(nmask, omask)
|
||||
|
||||
let fdi = signalfd(-1, nmask, 0).int
|
||||
if fdi == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fdi.cint)
|
||||
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
|
||||
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
||||
epv.data.u64 = fdi.uint
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
s.setKey(fdi, signal, {Event.Signal}, signal, data)
|
||||
inc(s.count)
|
||||
result = fdi
|
||||
|
||||
proc registerProcess*[T](s: Selector, pid: int,
|
||||
data: T): int {.discardable.} =
|
||||
var
|
||||
nmask: Sigset
|
||||
omask: Sigset
|
||||
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, posix.SIGCHLD)
|
||||
blockSignals(nmask, omask)
|
||||
|
||||
let fdi = signalfd(-1, nmask, 0).int
|
||||
if fdi == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fdi.cint)
|
||||
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
|
||||
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
||||
epv.data.u64 = fdi.uint
|
||||
epv.events = EPOLLIN or EPOLLRDHUP
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
s.setKey(fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
|
||||
inc(s.count)
|
||||
result = fdi
|
||||
|
||||
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
|
||||
let fdi = int(ev.efd)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
s.setKey(fdi, fdi, {Event.User}, 0, data)
|
||||
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
||||
epv.data.u64 = ev.efd.uint
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, ev.efd, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
inc(s.count)
|
||||
|
||||
proc flush*[T](s: Selector[T]) =
|
||||
discard
|
||||
|
||||
proc selectInto*[T](s: Selector[T], timeout: int,
|
||||
results: var openarray[ReadyKey[T]]): int =
|
||||
var
|
||||
resTable: array[MAX_EPOLL_RESULT_EVENTS, epoll_event]
|
||||
maxres = MAX_EPOLL_RESULT_EVENTS
|
||||
events: set[Event] = {}
|
||||
i, k: int
|
||||
|
||||
if maxres > len(results):
|
||||
maxres = len(results)
|
||||
|
||||
let count = epoll_wait(s.epollFD, addr(resTable[0]), maxres.cint,
|
||||
timeout.cint)
|
||||
if count < 0:
|
||||
result = 0
|
||||
let err = osLastError()
|
||||
if cint(err) != EINTR:
|
||||
raiseOSError(err)
|
||||
elif count == 0:
|
||||
result = 0
|
||||
else:
|
||||
i = 0
|
||||
k = 0
|
||||
while i < count:
|
||||
let fdi = int(resTable[i].data.u64)
|
||||
let pevents = resTable[i].events
|
||||
var skey = addr(s.fds[fdi])
|
||||
doAssert(skey.ident != 0)
|
||||
events = {}
|
||||
|
||||
if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
|
||||
events.incl(Event.Error)
|
||||
if (pevents and EPOLLOUT) != 0:
|
||||
events.incl(Event.Write)
|
||||
if (pevents and EPOLLIN) != 0:
|
||||
if Event.Read in skey.events:
|
||||
events.incl(Event.Read)
|
||||
elif Event.Timer in skey.events:
|
||||
var data: uint64 = 0
|
||||
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||
raiseOSError(osLastError())
|
||||
events = {Event.Timer}
|
||||
elif Event.Signal in skey.events:
|
||||
var data = SignalFdInfo()
|
||||
if posix.read(fdi.cint, addr data,
|
||||
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
|
||||
raiseOsError(osLastError())
|
||||
events = {Event.Signal}
|
||||
elif Event.Process in skey.events:
|
||||
var data = SignalFdInfo()
|
||||
if posix.read(fdi.cint, addr data,
|
||||
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
|
||||
raiseOsError(osLastError())
|
||||
if cast[int](data.ssi_pid) == skey.param:
|
||||
events = {Event.Process}
|
||||
else:
|
||||
inc(i)
|
||||
continue
|
||||
elif Event.User in skey.events:
|
||||
var data: uint = 0
|
||||
if posix.read(fdi.cint, addr data, sizeof(uint)) != sizeof(uint):
|
||||
let err = osLastError()
|
||||
if err == OSErrorCode(EAGAIN):
|
||||
inc(i)
|
||||
continue
|
||||
else:
|
||||
raiseOSError(err)
|
||||
events = {Event.User}
|
||||
|
||||
skey.key.events = events
|
||||
results[k] = skey.key
|
||||
inc(k)
|
||||
|
||||
if Event.Oneshot in skey.events:
|
||||
var epv = epoll_event()
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
discard posix.close(fdi.cint)
|
||||
skey.ident = 0
|
||||
skey.events = {}
|
||||
dec(s.count)
|
||||
inc(i)
|
||||
result = k
|
||||
|
||||
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
|
||||
result = newSeq[ReadyKey[T]](MAX_EPOLL_RESULT_EVENTS)
|
||||
let count = selectInto(s, timeout, result)
|
||||
result.setLen(count)
|
||||
|
||||
template isEmpty*[T](s: Selector[T]): bool =
|
||||
(s.count == 0)
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value,
|
||||
body: untyped) =
|
||||
mixin checkFd
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
if s.fds[fdi].ident != 0:
|
||||
var value = addr(s.fds[fdi].key.data)
|
||||
body
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value, body1,
|
||||
body2: untyped) =
|
||||
mixin checkFd
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
if s.fds[fdi].ident != 0:
|
||||
var value = addr(s.fds[fdi].key.data)
|
||||
body1
|
||||
else:
|
||||
body2
|
||||
439
lib/pure/ioselects/ioselectors_kqueue.nim
Normal file
439
lib/pure/ioselects/ioselectors_kqueue.nim
Normal file
@@ -0,0 +1,439 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2016 Eugene Kabanov
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# This module implements BSD kqueue().
|
||||
|
||||
import posix, times, kqueue
|
||||
|
||||
const
|
||||
# Maximum number of cached changes.
|
||||
MAX_KQUEUE_CHANGE_EVENTS = 64
|
||||
# Maximum number of events that can be returned.
|
||||
MAX_KQUEUE_RESULT_EVENTS = 64
|
||||
|
||||
when defined(macosx) or defined(freebsd):
|
||||
when defined(macosx):
|
||||
const MAX_DESCRIPTORS_ID = 29 # KERN_MAXFILESPERPROC (MacOS)
|
||||
else:
|
||||
const MAX_DESCRIPTORS_ID = 27 # KERN_MAXFILESPERPROC (FreeBSD)
|
||||
proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int,
|
||||
newp: pointer, newplen: int): cint
|
||||
{.importc: "sysctl",header: """#include <sys/types.h>
|
||||
#include <sys/sysctl.h>"""}
|
||||
elif defined(netbsd) or defined(openbsd):
|
||||
# OpenBSD and NetBSD don't have KERN_MAXFILESPERPROC, so we are using
|
||||
# KERN_MAXFILES, because KERN_MAXFILES is always bigger,
|
||||
# than KERN_MAXFILESPERPROC.
|
||||
const MAX_DESCRIPTORS_ID = 7 # KERN_MAXFILES
|
||||
proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int,
|
||||
newp: pointer, newplen: int): cint
|
||||
{.importc: "sysctl",header: """#include <sys/param.h>
|
||||
#include <sys/sysctl.h>"""}
|
||||
|
||||
when hasThreadSupport:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
kqFD : cint
|
||||
maxFD : int
|
||||
changesTable: array[MAX_KQUEUE_CHANGE_EVENTS, KEvent]
|
||||
changesCount: int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
count: int
|
||||
changesLock: Lock
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
kqFD : cint
|
||||
maxFD : int
|
||||
changesTable: array[MAX_KQUEUE_CHANGE_EVENTS, KEvent]
|
||||
changesCount: int
|
||||
fds: seq[SelectorKey[T]]
|
||||
count: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
|
||||
type
|
||||
SelectEventImpl = object
|
||||
rfd: cint
|
||||
wfd: cint
|
||||
# SelectEvent is declared as `ptr` to be placed in `shared memory`,
|
||||
# so you can share one SelectEvent handle between threads.
|
||||
type SelectEvent* = ptr SelectEventImpl
|
||||
|
||||
proc newSelector*[T](): Selector[T] =
|
||||
var maxFD = 0.cint
|
||||
var size = sizeof(cint)
|
||||
var namearr = [1.cint, MAX_DESCRIPTORS_ID.cint]
|
||||
# Obtain maximum number of file descriptors for process
|
||||
if sysctl(addr(namearr[0]), 2, cast[pointer](addr maxFD), addr size,
|
||||
nil, 0) != 0:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
var kqFD = kqueue()
|
||||
if kqFD < 0:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
when hasThreadSupport:
|
||||
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
|
||||
result.kqFD = kqFD
|
||||
result.maxFD = maxFD.int
|
||||
result.fds = allocSharedArray[SelectorKey[T]](maxFD)
|
||||
initLock(result.changesLock)
|
||||
else:
|
||||
result = Selector[T]()
|
||||
result.kqFD = kqFD
|
||||
result.maxFD = maxFD.int
|
||||
result.fds = newSeq[SelectorKey[T]](maxFD)
|
||||
|
||||
proc close*[T](s: Selector[T]) =
|
||||
if posix.close(s.kqFD) != 0:
|
||||
raiseOSError(osLastError())
|
||||
when hasThreadSupport:
|
||||
deinitLock(s.changesLock)
|
||||
deallocSharedArray(s.fds)
|
||||
deallocShared(cast[pointer](s))
|
||||
|
||||
proc newSelectEvent*(): SelectEvent =
|
||||
var fds: array[2, cint]
|
||||
if posix.pipe(fds) == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fds[0])
|
||||
setNonBlocking(fds[1])
|
||||
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
|
||||
result.rfd = fds[0]
|
||||
result.wfd = fds[1]
|
||||
|
||||
proc setEvent*(ev: SelectEvent) =
|
||||
var data: uint64 = 1
|
||||
if posix.write(ev.wfd, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(ev: SelectEvent) =
|
||||
discard posix.close(cint(ev.rfd))
|
||||
discard posix.close(cint(ev.wfd))
|
||||
deallocShared(cast[pointer](ev))
|
||||
|
||||
template checkFd(s, f) =
|
||||
if f >= s.maxFD:
|
||||
raise newException(ValueError, "Maximum file descriptors exceeded")
|
||||
|
||||
when hasThreadSupport:
|
||||
template withChangeLock[T](s: Selector[T], body: untyped) =
|
||||
acquire(s.changesLock)
|
||||
{.locks: [s.changesLock].}:
|
||||
try:
|
||||
body
|
||||
finally:
|
||||
release(s.changesLock)
|
||||
else:
|
||||
template withChangeLock(s, body: untyped) =
|
||||
body
|
||||
|
||||
template modifyKQueue[T](s: Selector[T], nident: uint, nfilter: cshort,
|
||||
nflags: cushort, nfflags: cuint, ndata: int,
|
||||
nudata: pointer) =
|
||||
mixin withChangeLock
|
||||
s.withChangeLock():
|
||||
s.changesTable[s.changesCount] = KEvent(ident: nident,
|
||||
filter: nfilter, flags: nflags,
|
||||
fflags: nfflags, data: ndata,
|
||||
udata: nudata)
|
||||
inc(s.changesCount)
|
||||
if s.changesCount == MAX_KQUEUE_CHANGE_EVENTS:
|
||||
if kevent(s.kqFD, addr(s.changesTable[0]), cint(s.changesCount),
|
||||
nil, 0, nil) == -1:
|
||||
raiseOSError(osLastError())
|
||||
s.changesCount = 0
|
||||
|
||||
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event], data: T) =
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
s.setKey(fdi, fdi, events, 0, data)
|
||||
if events != {}:
|
||||
if Event.Read in events:
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
|
||||
inc(s.count)
|
||||
if Event.Write in events:
|
||||
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_ADD, 0, 0, nil)
|
||||
inc(s.count)
|
||||
|
||||
proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event]) =
|
||||
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
|
||||
Event.User, Event.Oneshot, Event.Error}
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
doAssert(pkey.events * maskEvents == {})
|
||||
|
||||
if pkey.events != events:
|
||||
if (Event.Read in pkey.events) and (Event.Read notin events):
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
if (Event.Write in pkey.events) and (Event.Write notin events):
|
||||
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
if (Event.Read notin pkey.events) and (Event.Read in events):
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
|
||||
inc(s.count)
|
||||
if (Event.Write notin pkey.events) and (Event.Write in events):
|
||||
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_ADD, 0, 0, nil)
|
||||
inc(s.count)
|
||||
pkey.events = events
|
||||
|
||||
proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
|
||||
data: T): int {.discardable.} =
|
||||
var fdi = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
|
||||
posix.IPPROTO_TCP).int
|
||||
if fdi == -1:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
|
||||
let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
|
||||
let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD
|
||||
|
||||
s.setKey(fdi, fdi, events, 0, data)
|
||||
# EVFILT_TIMER on Open/Net(BSD) has granularity of only milliseconds,
|
||||
# but MacOS and FreeBSD allow use `0` as `fflags` to use milliseconds
|
||||
# too
|
||||
modifyKQueue(s, fdi.uint, EVFILT_TIMER, flags, 0, cint(timeout), nil)
|
||||
inc(s.count)
|
||||
result = fdi
|
||||
|
||||
proc registerSignal*[T](s: Selector[T], signal: int,
|
||||
data: T): int {.discardable.} =
|
||||
var fdi = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
|
||||
posix.IPPROTO_TCP).int
|
||||
if fdi == -1:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
|
||||
s.setKey(fdi, signal, {Event.Signal}, signal, data)
|
||||
var nmask, omask: Sigset
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, cint(signal))
|
||||
blockSignals(nmask, omask)
|
||||
# to be compatible with linux semantic we need to "eat" signals
|
||||
posix.signal(cint(signal), SIG_IGN)
|
||||
modifyKQueue(s, signal.uint, EVFILT_SIGNAL, EV_ADD, 0, 0,
|
||||
cast[pointer](fdi))
|
||||
inc(s.count)
|
||||
result = fdi
|
||||
|
||||
proc registerProcess*[T](s: Selector[T], pid: int,
|
||||
data: T): int {.discardable.} =
|
||||
var fdi = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
|
||||
posix.IPPROTO_TCP).int
|
||||
if fdi == -1:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
|
||||
var kflags: cushort = EV_ONESHOT or EV_ADD
|
||||
setKey(s, fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
|
||||
modifyKQueue(s, pid.uint, EVFILT_PROC, kflags, NOTE_EXIT, 0,
|
||||
cast[pointer](fdi))
|
||||
inc(s.count)
|
||||
result = fdi
|
||||
|
||||
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
|
||||
let fdi = ev.rfd.int
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
setKey(s, fdi, fdi, {Event.User}, 0, data)
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
|
||||
inc(s.count)
|
||||
|
||||
proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
|
||||
if pkey.events != {}:
|
||||
if pkey.events * {Event.Read, Event.Write} != {}:
|
||||
if Event.Read in pkey.events:
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
if Event.Write in pkey.events:
|
||||
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
elif Event.Timer in pkey.events:
|
||||
discard posix.close(cint(pkey.key.fd))
|
||||
modifyKQueue(s, fdi.uint, EVFILT_TIMER, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
elif Event.Signal in pkey.events:
|
||||
var nmask, omask: Sigset
|
||||
var signal = cint(pkey.param)
|
||||
discard sigemptyset(nmask)
|
||||
discard sigemptyset(omask)
|
||||
discard sigaddset(nmask, signal)
|
||||
unblockSignals(nmask, omask)
|
||||
posix.signal(signal, SIG_DFL)
|
||||
discard posix.close(cint(pkey.key.fd))
|
||||
modifyKQueue(s, fdi.uint, EVFILT_SIGNAL, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
elif Event.Process in pkey.events:
|
||||
discard posix.close(cint(pkey.key.fd))
|
||||
modifyKQueue(s, fdi.uint, EVFILT_PROC, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
elif Event.User in pkey.events:
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
|
||||
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
|
||||
let fdi = int(ev.rfd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
doAssert(Event.User in pkey.events)
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
|
||||
dec(s.count)
|
||||
|
||||
proc flush*[T](s: Selector[T]) =
|
||||
s.withChangeLock():
|
||||
var tv = Timespec()
|
||||
if kevent(s.kqFD, addr(s.changesTable[0]), cint(s.changesCount),
|
||||
nil, 0, addr tv) == -1:
|
||||
raiseOSError(osLastError())
|
||||
s.changesCount = 0
|
||||
|
||||
proc selectInto*[T](s: Selector[T], timeout: int,
|
||||
results: var openarray[ReadyKey[T]]): int =
|
||||
var
|
||||
tv: Timespec
|
||||
resTable: array[MAX_KQUEUE_RESULT_EVENTS, KEvent]
|
||||
ptv = addr tv
|
||||
maxres = MAX_KQUEUE_RESULT_EVENTS
|
||||
|
||||
if timeout != -1:
|
||||
if timeout >= 1000:
|
||||
tv.tv_sec = (timeout div 1_000).Time
|
||||
tv.tv_nsec = (timeout %% 1_000) * 1_000_000
|
||||
else:
|
||||
tv.tv_sec = 0.Time
|
||||
tv.tv_nsec = timeout * 1_000_000
|
||||
else:
|
||||
ptv = nil
|
||||
|
||||
if maxres > len(results):
|
||||
maxres = len(results)
|
||||
|
||||
var count = 0
|
||||
s.withChangeLock():
|
||||
count = kevent(s.kqFD, addr(s.changesTable[0]), cint(s.changesCount),
|
||||
addr(resTable[0]), cint(maxres), ptv)
|
||||
s.changesCount = 0
|
||||
|
||||
if count < 0:
|
||||
result = 0
|
||||
let err = osLastError()
|
||||
if cint(err) != EINTR:
|
||||
raiseOSError(err)
|
||||
elif count == 0:
|
||||
result = 0
|
||||
else:
|
||||
var i = 0
|
||||
var k = 0
|
||||
var pkey: ptr SelectorKey[T]
|
||||
while i < count:
|
||||
let kevent = addr(resTable[i])
|
||||
if (kevent.flags and EV_ERROR) == 0:
|
||||
case kevent.filter:
|
||||
of EVFILT_READ:
|
||||
pkey = addr(s.fds[kevent.ident.int])
|
||||
pkey.key.events = {Event.Read}
|
||||
if Event.User in pkey.events:
|
||||
var data: uint64 = 0
|
||||
if posix.read(kevent.ident.cint, addr data,
|
||||
sizeof(uint64)) != sizeof(uint64):
|
||||
let err = osLastError()
|
||||
if err == OSErrorCode(EAGAIN):
|
||||
# someone already consumed event data
|
||||
inc(i)
|
||||
continue
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
pkey.key.events = {Event.User}
|
||||
of EVFILT_WRITE:
|
||||
pkey = addr(s.fds[kevent.ident.int])
|
||||
pkey.key.events = {Event.Write}
|
||||
of EVFILT_TIMER:
|
||||
pkey = addr(s.fds[kevent.ident.int])
|
||||
if Event.Oneshot in pkey.events:
|
||||
if posix.close(cint(pkey.ident)) == -1:
|
||||
raiseOSError(osLastError())
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
dec(s.count)
|
||||
pkey.key.events = {Event.Timer}
|
||||
of EVFILT_VNODE:
|
||||
pkey = addr(s.fds[kevent.ident.int])
|
||||
pkey.key.events = {Event.Vnode}
|
||||
of EVFILT_SIGNAL:
|
||||
pkey = addr(s.fds[cast[int](kevent.udata)])
|
||||
pkey.key.events = {Event.Signal}
|
||||
of EVFILT_PROC:
|
||||
pkey = addr(s.fds[cast[int](kevent.udata)])
|
||||
if posix.close(cint(pkey.ident)) == -1:
|
||||
raiseOSError(osLastError())
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
dec(s.count)
|
||||
pkey.key.events = {Event.Process}
|
||||
else:
|
||||
raise newException(ValueError, "Unsupported kqueue filter in queue")
|
||||
|
||||
if (kevent.flags and EV_EOF) != 0:
|
||||
pkey.key.events.incl(Event.Error)
|
||||
|
||||
results[k] = pkey.key
|
||||
inc(k)
|
||||
inc(i)
|
||||
result = k
|
||||
|
||||
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
|
||||
result = newSeq[ReadyKey[T]](MAX_KQUEUE_RESULT_EVENTS)
|
||||
let count = selectInto(s, timeout, result)
|
||||
result.setLen(count)
|
||||
|
||||
template isEmpty*[T](s: Selector[T]): bool =
|
||||
(s.count == 0)
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value,
|
||||
body: untyped) =
|
||||
mixin checkFd
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
if s.fds[fdi].ident != 0:
|
||||
var value = addr(s.fds[fdi].key.data)
|
||||
body
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value, body1,
|
||||
body2: untyped) =
|
||||
mixin checkFd
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
if s.fds[fdi].ident != 0:
|
||||
var value = addr(s.fds[fdi].key.data)
|
||||
body1
|
||||
else:
|
||||
body2
|
||||
295
lib/pure/ioselects/ioselectors_poll.nim
Normal file
295
lib/pure/ioselects/ioselectors_poll.nim
Normal file
@@ -0,0 +1,295 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2016 Eugene Kabanov
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# This module implements Posix poll().
|
||||
|
||||
import posix, times
|
||||
|
||||
# Maximum number of events that can be returned
|
||||
const MAX_POLL_RESULT_EVENTS = 64
|
||||
|
||||
when hasThreadSupport:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
maxFD : int
|
||||
pollcnt: int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
pollfds: ptr SharedArray[TPollFd]
|
||||
count: int
|
||||
lock: Lock
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
maxFD : int
|
||||
pollcnt: int
|
||||
fds: seq[SelectorKey[T]]
|
||||
pollfds: seq[TPollFd]
|
||||
count: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
|
||||
type
|
||||
SelectEventImpl = object
|
||||
rfd: cint
|
||||
wfd: cint
|
||||
SelectEvent* = ptr SelectEventImpl
|
||||
|
||||
var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
|
||||
header: "<sys/resource.h>".}: cint
|
||||
type
|
||||
rlimit {.importc: "struct rlimit",
|
||||
header: "<sys/resource.h>", pure, final.} = object
|
||||
rlim_cur: int
|
||||
rlim_max: int
|
||||
proc getrlimit(resource: cint, rlp: var rlimit): cint
|
||||
{.importc: "getrlimit",header: "<sys/resource.h>".}
|
||||
|
||||
when hasThreadSupport:
|
||||
template withPollLock[T](s: Selector[T], body: untyped) =
|
||||
acquire(s.lock)
|
||||
{.locks: [s.lock].}:
|
||||
try:
|
||||
body
|
||||
finally:
|
||||
release(s.lock)
|
||||
else:
|
||||
template withPollLock(s, body: untyped) =
|
||||
body
|
||||
|
||||
proc newSelector*[T](): Selector[T] =
|
||||
var a = rlimit()
|
||||
if getrlimit(RLIMIT_NOFILE, a) != 0:
|
||||
raiseOsError(osLastError())
|
||||
var maxFD = int(a.rlim_max)
|
||||
|
||||
when hasThreadSupport:
|
||||
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
|
||||
result.maxFD = maxFD
|
||||
result.fds = allocSharedArray[SelectorKey[T]](maxFD)
|
||||
result.pollfds = allocSharedArray[TPollFd](maxFD)
|
||||
initLock(result.lock)
|
||||
else:
|
||||
result = Selector[T]()
|
||||
result.maxFD = maxFD
|
||||
result.fds = newSeq[SelectorKey[T]](maxFD)
|
||||
result.pollfds = newSeq[TPollFd](maxFD)
|
||||
|
||||
proc close*[T](s: Selector[T]) =
|
||||
when hasThreadSupport:
|
||||
deinitLock(s.lock)
|
||||
deallocSharedArray(s.fds)
|
||||
deallocSharedArray(s.pollfds)
|
||||
deallocShared(cast[pointer](s))
|
||||
|
||||
template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
|
||||
withPollLock(s):
|
||||
var pollev: cshort = 0
|
||||
if Event.Read in events: pollev = pollev or POLLIN
|
||||
if Event.Write in events: pollev = pollev or POLLOUT
|
||||
s.pollfds[s.pollcnt].fd = cint(sock)
|
||||
s.pollfds[s.pollcnt].events = pollev
|
||||
inc(s.count)
|
||||
inc(s.pollcnt)
|
||||
|
||||
template pollUpdate[T](s: Selector[T], sock: cint, events: set[Event]) =
|
||||
withPollLock(s):
|
||||
var i = 0
|
||||
var pollev: cshort = 0
|
||||
if Event.Read in events: pollev = pollev or POLLIN
|
||||
if Event.Write in events: pollev = pollev or POLLOUT
|
||||
|
||||
while i < s.pollcnt:
|
||||
if s.pollfds[i].fd == sock:
|
||||
s.pollfds[i].events = pollev
|
||||
break
|
||||
inc(i)
|
||||
|
||||
if i == s.pollcnt:
|
||||
raise newException(ValueError, "Descriptor is not registered in queue")
|
||||
|
||||
template pollRemove[T](s: Selector[T], sock: cint) =
|
||||
withPollLock(s):
|
||||
var i = 0
|
||||
while i < s.pollcnt:
|
||||
if s.pollfds[i].fd == sock:
|
||||
if i == s.pollcnt - 1:
|
||||
s.pollfds[i].fd = 0
|
||||
s.pollfds[i].events = 0
|
||||
s.pollfds[i].revents = 0
|
||||
else:
|
||||
while i < (s.pollcnt - 1):
|
||||
s.pollfds[i].fd = s.pollfds[i + 1].fd
|
||||
s.pollfds[i].events = s.pollfds[i + 1].events
|
||||
inc(i)
|
||||
break
|
||||
inc(i)
|
||||
dec(s.pollcnt)
|
||||
dec(s.count)
|
||||
|
||||
template checkFd(s, f) =
|
||||
if f >= s.maxFD:
|
||||
raise newException(ValueError, "Maximum file descriptors exceeded")
|
||||
|
||||
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event], data: T) =
|
||||
var fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
s.setKey(fdi, fdi, events, 0, data)
|
||||
if events != {}: s.pollAdd(fdi.cint, events)
|
||||
|
||||
proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event]) =
|
||||
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
|
||||
Event.User, Event.Oneshot, Event.Error}
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
doAssert(pkey.events * maskEvents == {})
|
||||
|
||||
if pkey.events != events:
|
||||
if pkey.events == {}:
|
||||
s.pollAdd(fd.cint, events)
|
||||
else:
|
||||
if events != {}:
|
||||
s.pollUpdate(fd.cint, events)
|
||||
else:
|
||||
s.pollRemove(fd.cint)
|
||||
pkey.events = events
|
||||
|
||||
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
|
||||
var fdi = int(ev.rfd)
|
||||
doAssert(s.fds[fdi].ident == 0)
|
||||
var events = {Event.User}
|
||||
setKey(s, fdi, fdi, events, 0, data)
|
||||
events.incl(Event.Read)
|
||||
s.pollAdd(fdi.cint, events)
|
||||
|
||||
proc flush*[T](s: Selector[T]) = discard
|
||||
|
||||
proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
s.pollRemove(fdi.cint)
|
||||
|
||||
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
|
||||
let fdi = int(ev.rfd)
|
||||
s.checkFd(fdi)
|
||||
var pkey = addr(s.fds[fdi])
|
||||
doAssert(pkey.ident != 0)
|
||||
doAssert(Event.User in pkey.events)
|
||||
pkey.ident = 0
|
||||
pkey.events = {}
|
||||
s.pollRemove(fdi.cint)
|
||||
|
||||
proc newSelectEvent*(): SelectEvent =
|
||||
var fds: array[2, cint]
|
||||
if posix.pipe(fds) == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fds[0])
|
||||
setNonBlocking(fds[1])
|
||||
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
|
||||
result.rfd = fds[0]
|
||||
result.wfd = fds[1]
|
||||
|
||||
proc setEvent*(ev: SelectEvent) =
|
||||
var data: uint64 = 1
|
||||
if posix.write(ev.wfd, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(ev: SelectEvent) =
|
||||
discard posix.close(cint(ev.rfd))
|
||||
discard posix.close(cint(ev.wfd))
|
||||
deallocShared(cast[pointer](ev))
|
||||
|
||||
proc selectInto*[T](s: Selector[T], timeout: int,
|
||||
results: var openarray[ReadyKey[T]]): int =
|
||||
var maxres = MAX_POLL_RESULT_EVENTS
|
||||
if maxres > len(results):
|
||||
maxres = len(results)
|
||||
|
||||
s.withPollLock():
|
||||
let count = posix.poll(addr(s.pollfds[0]), Tnfds(s.pollcnt), timeout)
|
||||
if count < 0:
|
||||
result = 0
|
||||
let err = osLastError()
|
||||
if err.cint == EINTR:
|
||||
discard
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
elif count == 0:
|
||||
result = 0
|
||||
else:
|
||||
var i = 0
|
||||
var k = 0
|
||||
var rindex = 0
|
||||
while (i < s.pollcnt) and (k < count) and (rindex < maxres):
|
||||
let revents = s.pollfds[i].revents
|
||||
if revents != 0:
|
||||
let fd = s.pollfds[i].fd
|
||||
var skey = addr(s.fds[fd])
|
||||
skey.key.events = {}
|
||||
|
||||
if (revents and POLLIN) != 0:
|
||||
skey.key.events.incl(Event.Read)
|
||||
if Event.User in skey.events:
|
||||
var data: uint64 = 0
|
||||
if posix.read(fd, addr data, sizeof(int)) != sizeof(int):
|
||||
let err = osLastError()
|
||||
if err != OSErrorCode(EAGAIN):
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
# someone already consumed event data
|
||||
inc(i)
|
||||
continue
|
||||
skey.key.events = {Event.User}
|
||||
if (revents and POLLOUT) != 0:
|
||||
skey.key.events.incl(Event.Write)
|
||||
if (revents and POLLERR) != 0 or (revents and POLLHUP) != 0 or
|
||||
(revents and POLLNVAL) != 0:
|
||||
skey.key.events.incl(Event.Error)
|
||||
results[rindex] = skey.key
|
||||
s.pollfds[i].revents = 0
|
||||
inc(rindex)
|
||||
inc(k)
|
||||
inc(i)
|
||||
result = k
|
||||
|
||||
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
|
||||
result = newSeq[ReadyKey[T]](MAX_POLL_RESULT_EVENTS)
|
||||
let count = selectInto(s, timeout, result)
|
||||
result.setLen(count)
|
||||
|
||||
template isEmpty*[T](s: Selector[T]): bool =
|
||||
(s.count == 0)
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value,
|
||||
body: untyped) =
|
||||
mixin checkFd
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
if s.fds[fdi].ident != 0:
|
||||
var value = addr(s.fds[fdi].key.data)
|
||||
body
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value, body1,
|
||||
body2: untyped) =
|
||||
mixin checkFd
|
||||
let fdi = int(fd)
|
||||
s.checkFd(fdi)
|
||||
if s.fds[fdi].ident != 0:
|
||||
var value = addr(s.fds[fdi].key.data)
|
||||
body1
|
||||
else:
|
||||
body2
|
||||
416
lib/pure/ioselects/ioselectors_select.nim
Normal file
416
lib/pure/ioselects/ioselectors_select.nim
Normal file
@@ -0,0 +1,416 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2016 Eugene Kabanov
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# This module implements Posix and Windows select().
|
||||
|
||||
import times, nativesockets
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
when defined(gcc):
|
||||
{.passL: "-lws2_32".}
|
||||
elif defined(vcc):
|
||||
{.passL: "ws2_32.lib".}
|
||||
const platformHeaders = """#include <winsock2.h>
|
||||
#include <windows.h>"""
|
||||
const EAGAIN = WSAEWOULDBLOCK
|
||||
else:
|
||||
const platformHeaders = """#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>"""
|
||||
type
|
||||
Fdset {.importc: "fd_set", header: platformHeaders, pure, final.} = object
|
||||
var
|
||||
FD_SETSIZE {.importc: "FD_SETSIZE", header: platformHeaders.}: cint
|
||||
|
||||
proc IOFD_SET(fd: SocketHandle, fdset: ptr Fdset)
|
||||
{.cdecl, importc: "FD_SET", header: platformHeaders, inline.}
|
||||
proc IOFD_CLR(fd: SocketHandle, fdset: ptr Fdset)
|
||||
{.cdecl, importc: "FD_CLR", header: platformHeaders, inline.}
|
||||
proc IOFD_ZERO(fdset: ptr Fdset)
|
||||
{.cdecl, importc: "FD_ZERO", header: platformHeaders, inline.}
|
||||
|
||||
when defined(windows):
|
||||
proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint
|
||||
{.stdcall, importc: "FD_ISSET", header: platformHeaders, inline.}
|
||||
proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset,
|
||||
timeout: ptr Timeval): cint
|
||||
{.stdcall, importc: "select", header: platformHeaders.}
|
||||
else:
|
||||
proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint
|
||||
{.cdecl, importc: "FD_ISSET", header: platformHeaders, inline.}
|
||||
proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset,
|
||||
timeout: ptr Timeval): cint
|
||||
{.cdecl, importc: "select", header: platformHeaders.}
|
||||
|
||||
when hasThreadSupport:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
rSet: FdSet
|
||||
wSet: FdSet
|
||||
eSet: FdSet
|
||||
maxFD: int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
count: int
|
||||
lock: Lock
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
type
|
||||
SelectorImpl[T] = object
|
||||
rSet: FdSet
|
||||
wSet: FdSet
|
||||
eSet: FdSet
|
||||
maxFD: int
|
||||
fds: seq[SelectorKey[T]]
|
||||
count: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
|
||||
type
|
||||
SelectEventImpl = object
|
||||
rsock: SocketHandle
|
||||
wsock: SocketHandle
|
||||
SelectEvent* = ptr SelectEventImpl
|
||||
|
||||
when hasThreadSupport:
|
||||
template withSelectLock[T](s: Selector[T], body: untyped) =
|
||||
acquire(s.lock)
|
||||
{.locks: [s.lock].}:
|
||||
try:
|
||||
body
|
||||
finally:
|
||||
release(s.lock)
|
||||
else:
|
||||
template withSelectLock[T](s: Selector[T], body: untyped) =
|
||||
body
|
||||
|
||||
proc newSelector*[T](): Selector[T] =
|
||||
when hasThreadSupport:
|
||||
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
|
||||
result.fds = allocSharedArray[SelectorKey[T]](FD_SETSIZE)
|
||||
initLock result.lock
|
||||
else:
|
||||
result = Selector[T]()
|
||||
result.fds = newSeq[SelectorKey[T]](FD_SETSIZE)
|
||||
|
||||
IOFD_ZERO(addr result.rSet)
|
||||
IOFD_ZERO(addr result.wSet)
|
||||
IOFD_ZERO(addr result.eSet)
|
||||
|
||||
proc close*[T](s: Selector[T]) =
|
||||
when hasThreadSupport:
|
||||
deallocSharedArray(s.fds)
|
||||
deallocShared(cast[pointer](s))
|
||||
|
||||
when defined(windows):
|
||||
proc newSelectEvent*(): SelectEvent =
|
||||
var ssock = newNativeSocket()
|
||||
var wsock = newNativeSocket()
|
||||
var rsock: SocketHandle = INVALID_SOCKET
|
||||
var saddr = Sockaddr_in()
|
||||
|
||||
saddr.sin_family = winlean.AF_INET
|
||||
saddr.sin_port = 0
|
||||
saddr.sin_addr.s_addr = INADDR_ANY
|
||||
if bindAddr(ssock, cast[ptr SockAddr](addr(saddr)),
|
||||
sizeof(saddr).SockLen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if winlean.listen(ssock, 1) == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
var namelen = sizeof(saddr).SockLen
|
||||
if getsockname(ssock, cast[ptr SockAddr](addr(saddr)),
|
||||
addr(namelen)) == -1'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
saddr.sin_addr.s_addr = 0x0100007F
|
||||
if winlean.connect(wsock, cast[ptr SockAddr](addr(saddr)),
|
||||
sizeof(saddr).SockLen) == -1:
|
||||
raiseOSError(osLastError())
|
||||
namelen = sizeof(saddr).SockLen
|
||||
rsock = winlean.accept(ssock, cast[ptr SockAddr](addr(saddr)),
|
||||
cast[ptr SockLen](addr(namelen)))
|
||||
if rsock == SocketHandle(-1):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if winlean.closesocket(ssock) == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
var mode = clong(1)
|
||||
if ioctlsocket(rsock, FIONBIO, addr(mode)) == -1:
|
||||
raiseOSError(osLastError())
|
||||
mode = clong(1)
|
||||
if ioctlsocket(wsock, FIONBIO, addr(mode)) == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
|
||||
result.rsock = rsock
|
||||
result.wsock = wsock
|
||||
|
||||
proc setEvent*(ev: SelectEvent) =
|
||||
var data: int = 1
|
||||
if winlean.send(ev.wsock, cast[pointer](addr data),
|
||||
cint(sizeof(int)), 0) != sizeof(int):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(ev: SelectEvent) =
|
||||
discard winlean.closesocket(ev.rsock)
|
||||
discard winlean.closesocket(ev.wsock)
|
||||
deallocShared(cast[pointer](ev))
|
||||
|
||||
else:
|
||||
proc newSelectEvent*(): SelectEvent =
|
||||
var fds: array[2, cint]
|
||||
if posix.pipe(fds) == -1:
|
||||
raiseOSError(osLastError())
|
||||
setNonBlocking(fds[0])
|
||||
setNonBlocking(fds[1])
|
||||
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
|
||||
result.rsock = SocketHandle(fds[0])
|
||||
result.wsock = SocketHandle(fds[1])
|
||||
|
||||
proc setEvent*(ev: SelectEvent) =
|
||||
var data: uint64 = 1
|
||||
if posix.write(cint(ev.wsock), addr data, sizeof(uint64)) != sizeof(uint64):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(ev: SelectEvent) =
|
||||
discard posix.close(cint(ev.rsock))
|
||||
discard posix.close(cint(ev.wsock))
|
||||
deallocShared(cast[pointer](ev))
|
||||
|
||||
proc setKey[T](s: Selector[T], fd: SocketHandle, events: set[Event], data: T) =
|
||||
var i = 0
|
||||
let fdi = int(fd)
|
||||
while i < FD_SETSIZE:
|
||||
if s.fds[i].ident == 0:
|
||||
var pkey = addr(s.fds[i])
|
||||
pkey.ident = fdi
|
||||
pkey.events = events
|
||||
pkey.key.fd = fd.int
|
||||
pkey.key.events = {}
|
||||
pkey.key.data = data
|
||||
break
|
||||
inc(i)
|
||||
if i == FD_SETSIZE:
|
||||
raise newException(ValueError, "Maximum numbers of fds exceeded")
|
||||
|
||||
proc getKey[T](s: Selector[T], fd: SocketHandle): ptr SelectorKey[T] =
|
||||
var i = 0
|
||||
let fdi = int(fd)
|
||||
while i < FD_SETSIZE:
|
||||
if s.fds[i].ident == fdi:
|
||||
result = addr(s.fds[i])
|
||||
break
|
||||
inc(i)
|
||||
doAssert(i < FD_SETSIZE, "Descriptor not registered in queue")
|
||||
|
||||
proc delKey[T](s: Selector[T], fd: SocketHandle) =
|
||||
var i = 0
|
||||
while i < FD_SETSIZE:
|
||||
if s.fds[i].ident == fd.int:
|
||||
s.fds[i].ident = 0
|
||||
s.fds[i].events = {}
|
||||
break
|
||||
inc(i)
|
||||
doAssert(i < FD_SETSIZE, "Descriptor not registered in queue")
|
||||
|
||||
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event], data: T) =
|
||||
when not defined(windows):
|
||||
let fdi = int(fd)
|
||||
s.withSelectLock():
|
||||
s.setKey(fd, events, data)
|
||||
when not defined(windows):
|
||||
if fdi > s.maxFD: s.maxFD = fdi
|
||||
if Event.Read in events:
|
||||
IOFD_SET(fd, addr s.rSet)
|
||||
inc(s.count)
|
||||
if Event.Write in events:
|
||||
IOFD_SET(fd, addr s.wSet)
|
||||
IOFD_SET(fd, addr s.eSet)
|
||||
inc(s.count)
|
||||
|
||||
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
|
||||
when not defined(windows):
|
||||
let fdi = int(ev.rsock)
|
||||
s.withSelectLock():
|
||||
s.setKey(ev.rsock, {Event.User}, data)
|
||||
when not defined(windows):
|
||||
if fdi > s.maxFD: s.maxFD = fdi
|
||||
IOFD_SET(ev.rsock, addr s.rSet)
|
||||
inc(s.count)
|
||||
|
||||
proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
|
||||
events: set[Event]) =
|
||||
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
|
||||
Event.User, Event.Oneshot, Event.Error}
|
||||
s.withSelectLock():
|
||||
var pkey = s.getKey(fd)
|
||||
doAssert(pkey.events * maskEvents == {})
|
||||
if pkey.events != events:
|
||||
if (Event.Read in pkey.events) and (Event.Read notin events):
|
||||
IOFD_CLR(fd, addr s.rSet)
|
||||
dec(s.count)
|
||||
if (Event.Write in pkey.events) and (Event.Write notin events):
|
||||
IOFD_CLR(fd, addr s.wSet)
|
||||
IOFD_CLR(fd, addr s.eSet)
|
||||
dec(s.count)
|
||||
if (Event.Read notin pkey.events) and (Event.Read in events):
|
||||
IOFD_SET(fd, addr s.rSet)
|
||||
inc(s.count)
|
||||
if (Event.Write notin pkey.events) and (Event.Write in events):
|
||||
IOFD_SET(fd, addr s.wSet)
|
||||
IOFD_SET(fd, addr s.eSet)
|
||||
inc(s.count)
|
||||
pkey.events = events
|
||||
|
||||
proc unregister*[T](s: Selector[T], fd: SocketHandle) =
|
||||
s.withSelectLock():
|
||||
var pkey = s.getKey(fd)
|
||||
if Event.Read in pkey.events:
|
||||
IOFD_CLR(fd, addr s.rSet)
|
||||
dec(s.count)
|
||||
if Event.Write in pkey.events:
|
||||
IOFD_CLR(fd, addr s.wSet)
|
||||
IOFD_CLR(fd, addr s.eSet)
|
||||
dec(s.count)
|
||||
s.delKey(fd)
|
||||
|
||||
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
|
||||
let fd = ev.rsock
|
||||
s.withSelectLock():
|
||||
IOFD_CLR(fd, addr s.rSet)
|
||||
dec(s.count)
|
||||
s.delKey(fd)
|
||||
|
||||
proc selectInto*[T](s: Selector[T], timeout: int,
|
||||
results: var openarray[ReadyKey[T]]): int =
|
||||
var tv = Timeval()
|
||||
var ptv = addr tv
|
||||
var rset, wset, eset: FdSet
|
||||
|
||||
if timeout != -1:
|
||||
tv.tv_sec = timeout.int32 div 1_000
|
||||
tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
|
||||
else:
|
||||
ptv = nil
|
||||
|
||||
s.withSelectLock():
|
||||
rset = s.rSet
|
||||
wset = s.wSet
|
||||
eset = s.eSet
|
||||
|
||||
var count = ioselect(cint(s.maxFD) + 1, addr(rset), addr(wset),
|
||||
addr(eset), ptv)
|
||||
if count < 0:
|
||||
result = 0
|
||||
when defined(windows):
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
let err = osLastError()
|
||||
if cint(err) != EINTR:
|
||||
raiseOSError(err)
|
||||
elif count == 0:
|
||||
result = 0
|
||||
else:
|
||||
var rindex = 0
|
||||
var i = 0
|
||||
var k = 0
|
||||
|
||||
while (i < FD_SETSIZE) and (k < count):
|
||||
if s.fds[i].ident != 0:
|
||||
var flag = false
|
||||
var pkey = addr(s.fds[i])
|
||||
pkey.key.events = {}
|
||||
let fd = SocketHandle(pkey.ident)
|
||||
if IOFD_ISSET(fd, addr rset) != 0:
|
||||
if Event.User in pkey.events:
|
||||
var data: uint64 = 0
|
||||
if recv(fd, cast[pointer](addr(data)),
|
||||
sizeof(uint64).cint, 0) != sizeof(uint64):
|
||||
let err = osLastError()
|
||||
if cint(err) != EAGAIN:
|
||||
raiseOSError(err)
|
||||
else:
|
||||
inc(i)
|
||||
inc(k)
|
||||
continue
|
||||
else:
|
||||
flag = true
|
||||
pkey.key.events = {Event.User}
|
||||
else:
|
||||
flag = true
|
||||
pkey.key.events = {Event.Read}
|
||||
if IOFD_ISSET(fd, addr wset) != 0:
|
||||
pkey.key.events.incl(Event.Write)
|
||||
if IOFD_ISSET(fd, addr eset) != 0:
|
||||
pkey.key.events.incl(Event.Error)
|
||||
flag = true
|
||||
if flag:
|
||||
results[rindex] = pkey.key
|
||||
inc(rindex)
|
||||
inc(k)
|
||||
inc(i)
|
||||
result = rindex
|
||||
|
||||
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
|
||||
result = newSeq[ReadyKey[T]](FD_SETSIZE)
|
||||
var count = selectInto(s, timeout, result)
|
||||
result.setLen(count)
|
||||
|
||||
proc flush*[T](s: Selector[T]) = discard
|
||||
|
||||
template isEmpty*[T](s: Selector[T]): bool =
|
||||
(s.count == 0)
|
||||
|
||||
when hasThreadSupport:
|
||||
template withSelectLock[T](s: Selector[T], body: untyped) =
|
||||
acquire(s.lock)
|
||||
{.locks: [s.lock].}:
|
||||
try:
|
||||
body
|
||||
finally:
|
||||
release(s.lock)
|
||||
else:
|
||||
template withSelectLock[T](s: Selector[T], body: untyped) =
|
||||
body
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value,
|
||||
body: untyped) =
|
||||
mixin withSelectLock
|
||||
s.withSelectLock():
|
||||
var value: ptr T
|
||||
let fdi = int(fd)
|
||||
var i = 0
|
||||
while i < FD_SETSIZE:
|
||||
if s.fds[i].ident == fdi:
|
||||
value = addr(s.fds[i].key.data)
|
||||
break
|
||||
inc(i)
|
||||
if i != FD_SETSIZE:
|
||||
body
|
||||
|
||||
template withData*[T](s: Selector[T], fd: SocketHandle, value,
|
||||
body1, body2: untyped) =
|
||||
mixin withSelectLock
|
||||
s.withSelectLock():
|
||||
var value: ptr T
|
||||
let fdi = int(fd)
|
||||
var i = 0
|
||||
while i < FD_SETSIZE:
|
||||
if s.fds[i].ident == fdi:
|
||||
value = addr(s.fds[i].key.data)
|
||||
break
|
||||
inc(i)
|
||||
if i != FD_SETSIZE:
|
||||
body1
|
||||
else:
|
||||
body2
|
||||
@@ -712,6 +712,7 @@ proc `%`*(b: bool): JsonNode =
|
||||
|
||||
proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JObject JsonNode`
|
||||
if keyvals.len == 0: return newJArray()
|
||||
result = newJObject()
|
||||
for key, val in items(keyVals): result.fields[key] = val
|
||||
|
||||
|
||||
@@ -66,13 +66,13 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
|
||||
if err != 0'i32:
|
||||
when useWinUnicode:
|
||||
var msgbuf: WideCString
|
||||
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
|
||||
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
|
||||
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||
result = $msgbuf
|
||||
if msgbuf != nil: localFree(cast[pointer](msgbuf))
|
||||
else:
|
||||
var msgbuf: cstring
|
||||
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
|
||||
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
|
||||
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||
result = $msgbuf
|
||||
if msgbuf != nil: localFree(msgbuf)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# Included by the ``os`` module but a module in its own right for NimScript
|
||||
# support.
|
||||
|
||||
when isMainModule:
|
||||
when not declared(os):
|
||||
{.pragma: rtl.}
|
||||
import strutils
|
||||
|
||||
|
||||
@@ -142,6 +142,9 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} =
|
||||
template charData*(my: XmlParser): string =
|
||||
## returns the character data for the events: ``xmlCharData``,
|
||||
## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not one
|
||||
## of those events. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData,
|
||||
xmlSpecial})
|
||||
my.a
|
||||
@@ -149,31 +152,49 @@ template charData*(my: XmlParser): string =
|
||||
template elementName*(my: XmlParser): string =
|
||||
## returns the element name for the events: ``xmlElementStart``,
|
||||
## ``xmlElementEnd``, ``xmlElementOpen``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not one
|
||||
## of those events. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen})
|
||||
my.a
|
||||
|
||||
template entityName*(my: XmlParser): string =
|
||||
## returns the entity name for the event: ``xmlEntity``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not
|
||||
## ``xmlEntity``. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind == xmlEntity)
|
||||
my.a
|
||||
|
||||
template attrKey*(my: XmlParser): string =
|
||||
## returns the attribute key for the event ``xmlAttribute``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not
|
||||
## ``xmlAttribute``. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind == xmlAttribute)
|
||||
my.a
|
||||
|
||||
template attrValue*(my: XmlParser): string =
|
||||
## returns the attribute value for the event ``xmlAttribute``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not
|
||||
## ``xmlAttribute``. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind == xmlAttribute)
|
||||
my.b
|
||||
|
||||
template piName*(my: XmlParser): string =
|
||||
## returns the processing instruction name for the event ``xmlPI``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not
|
||||
## ``xmlPI``. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind == xmlPI)
|
||||
my.a
|
||||
|
||||
template piRest*(my: XmlParser): string =
|
||||
## returns the rest of the processing instruction for the event ``xmlPI``
|
||||
## Raises an assertion in debug mode if ``my.kind`` is not
|
||||
## ``xmlPI``. In release mode, this will not trigger an error
|
||||
## but the value returned will not be valid.
|
||||
assert(my.kind == xmlPI)
|
||||
my.b
|
||||
|
||||
|
||||
@@ -1841,8 +1841,8 @@ when isMainModule:
|
||||
result.add ", "
|
||||
|
||||
result.add case n:
|
||||
of 2: c[0].toLower & ": '" & c[1] & "'"
|
||||
of 1: c[0].toLower & ": ''"
|
||||
of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'"
|
||||
of 1: toLowerAscii(c[0]) & ": ''"
|
||||
else: ""
|
||||
|
||||
assert("Var1=key1;var2=Key2; VAR3".
|
||||
|
||||
@@ -26,6 +26,12 @@ include "system/inclrtl"
|
||||
|
||||
{.pop.}
|
||||
|
||||
# Support old split with set[char]
|
||||
when defined(nimOldSplit):
|
||||
{.pragma: deprecatedSplit, deprecated.}
|
||||
else:
|
||||
{.pragma: deprecatedSplit.}
|
||||
|
||||
type
|
||||
CharSet* {.deprecated.} = set[char] # for compatibility with Nim
|
||||
{.deprecated: [TCharSet: CharSet].}
|
||||
@@ -64,8 +70,8 @@ const
|
||||
## doAssert "01234".find(invalid) == -1
|
||||
## doAssert "01A34".find(invalid) == 2
|
||||
|
||||
proc isAlpha*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsAlphaChar".}=
|
||||
proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsAlphaAsciiChar".}=
|
||||
## Checks whether or not `c` is alphabetical.
|
||||
##
|
||||
## This checks a-z, A-Z ASCII characters only.
|
||||
@@ -85,27 +91,27 @@ proc isDigit*(c: char): bool {.noSideEffect, procvar,
|
||||
## This checks 0-9 ASCII characters only.
|
||||
return c in Digits
|
||||
|
||||
proc isSpace*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsSpaceChar".}=
|
||||
proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsSpaceAsciiChar".}=
|
||||
## Checks whether or not `c` is a whitespace character.
|
||||
return c in Whitespace
|
||||
|
||||
proc isLower*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsLowerChar".}=
|
||||
proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsLowerAsciiChar".}=
|
||||
## Checks whether or not `c` is a lower case character.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
return c in {'a'..'z'}
|
||||
|
||||
proc isUpper*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsUpperChar".}=
|
||||
proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsUpperAsciiChar".}=
|
||||
## Checks whether or not `c` is an upper case character.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
return c in {'A'..'Z'}
|
||||
|
||||
proc isAlpha*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsAlphaStr".}=
|
||||
proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsAlphaAsciiStr".}=
|
||||
## Checks whether or not `s` is alphabetical.
|
||||
##
|
||||
## This checks a-z, A-Z ASCII characters only.
|
||||
@@ -117,7 +123,7 @@ proc isAlpha*(s: string): bool {.noSideEffect, procvar,
|
||||
|
||||
result = true
|
||||
for c in s:
|
||||
result = c.isAlpha() and result
|
||||
result = c.isAlphaAscii() and result
|
||||
|
||||
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsAlphaNumericStr".}=
|
||||
@@ -149,8 +155,8 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
|
||||
for c in s:
|
||||
result = c.isDigit() and result
|
||||
|
||||
proc isSpace*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsSpaceStr".}=
|
||||
proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsSpaceAsciiStr".}=
|
||||
## Checks whether or not `s` is completely whitespace.
|
||||
##
|
||||
## Returns true if all characters in `s` are whitespace
|
||||
@@ -160,11 +166,11 @@ proc isSpace*(s: string): bool {.noSideEffect, procvar,
|
||||
|
||||
result = true
|
||||
for c in s:
|
||||
if not c.isSpace():
|
||||
if not c.isSpaceAscii():
|
||||
return false
|
||||
|
||||
proc isLower*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsLowerStr".}=
|
||||
proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsLowerAsciiStr".}=
|
||||
## Checks whether or not `s` contains all lower case characters.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
@@ -175,10 +181,10 @@ proc isLower*(s: string): bool {.noSideEffect, procvar,
|
||||
|
||||
result = true
|
||||
for c in s:
|
||||
result = c.isLower() and result
|
||||
result = c.isLowerAscii() and result
|
||||
|
||||
proc isUpper*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsUpperStr".}=
|
||||
proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuIsUpperAsciiStr".}=
|
||||
## Checks whether or not `s` contains all upper case characters.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
@@ -189,10 +195,10 @@ proc isUpper*(s: string): bool {.noSideEffect, procvar,
|
||||
|
||||
result = true
|
||||
for c in s:
|
||||
result = c.isUpper() and result
|
||||
result = c.isUpperAscii() and result
|
||||
|
||||
proc toLower*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToLowerChar".} =
|
||||
proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToLowerAsciiChar".} =
|
||||
## Converts `c` into lower case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||
@@ -203,8 +209,8 @@ proc toLower*(c: char): char {.noSideEffect, procvar,
|
||||
else:
|
||||
result = c
|
||||
|
||||
proc toLower*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToLowerStr".} =
|
||||
proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToLowerAsciiStr".} =
|
||||
## Converts `s` into lower case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||
@@ -212,10 +218,10 @@ proc toLower*(s: string): string {.noSideEffect, procvar,
|
||||
## character.
|
||||
result = newString(len(s))
|
||||
for i in 0..len(s) - 1:
|
||||
result[i] = toLower(s[i])
|
||||
result[i] = toLowerAscii(s[i])
|
||||
|
||||
proc toUpper*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToUpperChar".} =
|
||||
proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToUpperAsciiChar".} =
|
||||
## Converts `c` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||
@@ -226,8 +232,8 @@ proc toUpper*(c: char): char {.noSideEffect, procvar,
|
||||
else:
|
||||
result = c
|
||||
|
||||
proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToUpperStr".} =
|
||||
proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToUpperAsciiStr".} =
|
||||
## Converts `s` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||
@@ -235,14 +241,145 @@ proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||
## character.
|
||||
result = newString(len(s))
|
||||
for i in 0..len(s) - 1:
|
||||
result[i] = toUpper(s[i])
|
||||
result[i] = toUpperAscii(s[i])
|
||||
|
||||
proc capitalize*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuCapitalize".} =
|
||||
proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuCapitalizeAscii".} =
|
||||
## Converts the first character of `s` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``.
|
||||
result = toUpper(s[0]) & substr(s, 1)
|
||||
result = toUpperAscii(s[0]) & substr(s, 1)
|
||||
|
||||
proc isSpace*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsSpaceChar".}=
|
||||
## Checks whether or not `c` is a whitespace character.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
|
||||
isSpaceAscii(c)
|
||||
|
||||
proc isLower*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsLowerChar".}=
|
||||
## Checks whether or not `c` is a lower case character.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
|
||||
isLowerAscii(c)
|
||||
|
||||
proc isUpper*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsUpperChar".}=
|
||||
## Checks whether or not `c` is an upper case character.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
|
||||
isUpperAscii(c)
|
||||
|
||||
proc isAlpha*(c: char): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsAlphaChar".}=
|
||||
## Checks whether or not `c` is alphabetical.
|
||||
##
|
||||
## This checks a-z, A-Z ASCII characters only.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
|
||||
isAlphaAscii(c)
|
||||
|
||||
proc isAlpha*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsAlphaStr".}=
|
||||
## Checks whether or not `s` is alphabetical.
|
||||
##
|
||||
## This checks a-z, A-Z ASCII characters only.
|
||||
## Returns true if all characters in `s` are
|
||||
## alphabetic and there is at least one character
|
||||
## in `s`.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
|
||||
isAlphaAscii(s)
|
||||
|
||||
proc isSpace*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsSpaceStr".}=
|
||||
## Checks whether or not `s` is completely whitespace.
|
||||
##
|
||||
## Returns true if all characters in `s` are whitespace
|
||||
## characters and there is at least one character in `s`.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
|
||||
isSpaceAscii(s)
|
||||
|
||||
proc isLower*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsLowerStr".}=
|
||||
## Checks whether or not `s` contains all lower case characters.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
## Returns true if all characters in `s` are lower case
|
||||
## and there is at least one character in `s`.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
|
||||
isLowerAscii(s)
|
||||
|
||||
proc isUpper*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuIsUpperStr".}=
|
||||
## Checks whether or not `s` contains all upper case characters.
|
||||
##
|
||||
## This checks ASCII characters only.
|
||||
## Returns true if all characters in `s` are upper case
|
||||
## and there is at least one character in `s`.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
|
||||
isUpperAscii(s)
|
||||
|
||||
proc toLower*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuToLowerChar".} =
|
||||
## Converts `c` into lower case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||
## <unicode.html#toLower>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
|
||||
toLowerAscii(c)
|
||||
|
||||
proc toLower*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuToLowerStr".} =
|
||||
## Converts `s` into lower case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||
## <unicode.html#toLower>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
|
||||
toLowerAscii(s)
|
||||
|
||||
proc toUpper*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuToUpperChar".} =
|
||||
## Converts `c` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||
## <unicode.html#toUpper>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
|
||||
toUpperAscii(c)
|
||||
|
||||
proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuToUpperStr".} =
|
||||
## Converts `s` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||
## <unicode.html#toUpper>`_ for a version that works for any Unicode
|
||||
## character.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
|
||||
toUpperAscii(s)
|
||||
|
||||
proc capitalize*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, deprecated, extern: "nsuCapitalize".} =
|
||||
## Converts the first character of `s` into upper case.
|
||||
##
|
||||
## This works only for the letters ``A-Z``.
|
||||
##
|
||||
## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead.
|
||||
capitalizeAscii(s)
|
||||
|
||||
proc normalize*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuNormalize".} =
|
||||
@@ -271,7 +408,7 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
|
||||
var i = 0
|
||||
var m = min(a.len, b.len)
|
||||
while i < m:
|
||||
result = ord(toLower(a[i])) - ord(toLower(b[i]))
|
||||
result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i]))
|
||||
if result != 0: return
|
||||
inc(i)
|
||||
result = a.len - b.len
|
||||
@@ -292,8 +429,8 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
|
||||
while true:
|
||||
while a[i] == '_': inc(i)
|
||||
while b[j] == '_': inc(j) # BUGFIX: typo
|
||||
var aa = toLower(a[i])
|
||||
var bb = toLower(b[j])
|
||||
var aa = toLowerAscii(a[i])
|
||||
var bb = toLowerAscii(b[j])
|
||||
result = ord(aa) - ord(bb)
|
||||
if result != 0 or aa == '\0': break
|
||||
inc(i)
|
||||
@@ -341,17 +478,63 @@ proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "
|
||||
if not c.isSpace():
|
||||
return false
|
||||
|
||||
proc substrEq(s: string, pos: int, substr: string): bool =
|
||||
var i = 0
|
||||
var length = substr.len
|
||||
while i < length and s[pos+i] == substr[i]:
|
||||
inc i
|
||||
|
||||
return i == length
|
||||
|
||||
# --------- Private templates for different split separators -----------
|
||||
|
||||
template stringHasSep(s: string, index: int, seps: set[char]): bool =
|
||||
s[index] in seps
|
||||
|
||||
template stringHasSep(s: string, index: int, sep: char): bool =
|
||||
s[index] == sep
|
||||
|
||||
template stringHasSep(s: string, index: int, sep: string): bool =
|
||||
s.substrEq(index, sep)
|
||||
|
||||
template splitCommon(s, sep, maxsplit, sepLen) =
|
||||
## Common code for split procedures
|
||||
var last = 0
|
||||
var splits = maxsplit
|
||||
|
||||
if len(s) > 0:
|
||||
while last <= len(s):
|
||||
var first = last
|
||||
while last < len(s) and not stringHasSep(s, last, sep):
|
||||
inc(last)
|
||||
if splits == 0: last = len(s)
|
||||
yield substr(s, first, last-1)
|
||||
if splits == 0: break
|
||||
dec(splits)
|
||||
inc(last, sepLen)
|
||||
|
||||
template oldSplit(s, seps, maxsplit) =
|
||||
var last = 0
|
||||
var splits = maxsplit
|
||||
assert(not ('\0' in seps))
|
||||
while last < len(s):
|
||||
while s[last] in seps: inc(last)
|
||||
var first = last
|
||||
while last < len(s) and s[last] notin seps: inc(last)
|
||||
if first <= last-1:
|
||||
if splits == 0: last = len(s)
|
||||
yield substr(s, first, last-1)
|
||||
if splits == 0: break
|
||||
dec(splits)
|
||||
|
||||
iterator split*(s: string, seps: set[char] = Whitespace,
|
||||
maxsplit: int = -1): string =
|
||||
## Splits the string `s` into substrings using a group of separators.
|
||||
##
|
||||
## Substrings are separated by a substring containing only `seps`. Note
|
||||
## that whole sequences of characters found in ``seps`` will be counted as
|
||||
## a single split point and leading/trailing separators will be ignored.
|
||||
## The following example:
|
||||
## Substrings are separated by a substring containing only `seps`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split(" this is an example "):
|
||||
## for word in split("this\lis an\texample"):
|
||||
## writeLine(stdout, word)
|
||||
##
|
||||
## ...generates this output:
|
||||
@@ -365,7 +548,7 @@ iterator split*(s: string, seps: set[char] = Whitespace,
|
||||
## And the following code:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split(";;this;is;an;;example;;;", {';'}):
|
||||
## for word in split("this:is;an$example", {';', ':', '$'}):
|
||||
## writeLine(stdout, word)
|
||||
##
|
||||
## ...produces the same output as the first example. The code:
|
||||
@@ -386,26 +569,26 @@ iterator split*(s: string, seps: set[char] = Whitespace,
|
||||
## "08"
|
||||
## "08.398990"
|
||||
##
|
||||
var last = 0
|
||||
var splits = maxsplit
|
||||
assert(not ('\0' in seps))
|
||||
while last < len(s):
|
||||
while s[last] in seps: inc(last)
|
||||
var first = last
|
||||
while last < len(s) and s[last] notin seps: inc(last) # BUGFIX!
|
||||
if first <= last-1:
|
||||
if splits == 0: last = len(s)
|
||||
yield substr(s, first, last-1)
|
||||
if splits == 0: break
|
||||
dec(splits)
|
||||
when defined(nimOldSplit):
|
||||
oldSplit(s, seps, maxsplit)
|
||||
else:
|
||||
splitCommon(s, seps, maxsplit, 1)
|
||||
|
||||
iterator splitWhitespace*(s: string): string =
|
||||
## Splits at whitespace.
|
||||
oldSplit(s, Whitespace, -1)
|
||||
|
||||
proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
|
||||
rtl, extern: "nsuSplitWhitespace".} =
|
||||
## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
|
||||
## iterator, but is a proc that returns a sequence of substrings.
|
||||
accumulateResult(splitWhitespace(s))
|
||||
|
||||
iterator split*(s: string, sep: char, maxsplit: int = -1): string =
|
||||
## Splits the string `s` into substrings using a single separator.
|
||||
##
|
||||
## Substrings are separated by the character `sep`.
|
||||
## Unlike the version of the iterator which accepts a set of separator
|
||||
## characters, this proc will not coalesce groups of the
|
||||
## separator, returning a string for each found character. The code:
|
||||
## The code:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split(";;this;is;an;;example;;;", ';'):
|
||||
@@ -425,56 +608,27 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string =
|
||||
## ""
|
||||
## ""
|
||||
##
|
||||
var last = 0
|
||||
var splits = maxsplit
|
||||
assert('\0' != sep)
|
||||
if len(s) > 0:
|
||||
# `<=` is correct here for the edge cases!
|
||||
while last <= len(s):
|
||||
var first = last
|
||||
while last < len(s) and s[last] != sep: inc(last)
|
||||
if splits == 0: last = len(s)
|
||||
yield substr(s, first, last-1)
|
||||
if splits == 0: break
|
||||
dec(splits)
|
||||
inc(last)
|
||||
|
||||
proc substrEq(s: string, pos: int, substr: string): bool =
|
||||
var i = 0
|
||||
var length = substr.len
|
||||
while i < length and s[pos+i] == substr[i]:
|
||||
inc i
|
||||
|
||||
return i == length
|
||||
splitCommon(s, sep, maxsplit, 1)
|
||||
|
||||
iterator split*(s: string, sep: string, maxsplit: int = -1): string =
|
||||
## Splits the string `s` into substrings using a string separator.
|
||||
##
|
||||
## Substrings are separated by the string `sep`.
|
||||
var last = 0
|
||||
var splits = maxsplit
|
||||
## The code:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## for word in split("thisDATAisDATAcorrupted", "DATA"):
|
||||
## writeLine(stdout, word)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block::
|
||||
## "this"
|
||||
## "is"
|
||||
## "corrupted"
|
||||
##
|
||||
|
||||
if len(s) > 0:
|
||||
while last <= len(s):
|
||||
var first = last
|
||||
while last < len(s) and not s.substrEq(last, sep):
|
||||
inc(last)
|
||||
if splits == 0: last = len(s)
|
||||
yield substr(s, first, last-1)
|
||||
if splits == 0: break
|
||||
dec(splits)
|
||||
inc(last, sep.len)
|
||||
|
||||
# --------- Private templates for different rsplit separators -----------
|
||||
|
||||
template stringHasSep(s: string, index: int, seps: set[char]): bool =
|
||||
s[index] in seps
|
||||
|
||||
template stringHasSep(s: string, index: int, sep: char): bool =
|
||||
s[index] == sep
|
||||
|
||||
template stringHasSep(s: string, index: int, sep: string): bool =
|
||||
s.substrEq(index, sep)
|
||||
splitCommon(s, sep, maxsplit, sep.len)
|
||||
|
||||
template rsplitCommon(s, sep, maxsplit, sepLen) =
|
||||
## Common code for rsplit functions
|
||||
@@ -2138,13 +2292,13 @@ when isMainModule:
|
||||
|
||||
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
|
||||
|
||||
doAssert isAlpha('r')
|
||||
doAssert isAlpha('A')
|
||||
doAssert(not isAlpha('$'))
|
||||
doAssert isAlphaAscii('r')
|
||||
doAssert isAlphaAscii('A')
|
||||
doAssert(not isAlphaAscii('$'))
|
||||
|
||||
doAssert isAlpha("Rasp")
|
||||
doAssert isAlpha("Args")
|
||||
doAssert(not isAlpha("$Tomato"))
|
||||
doAssert isAlphaAscii("Rasp")
|
||||
doAssert isAlphaAscii("Args")
|
||||
doAssert(not isAlphaAscii("$Tomato"))
|
||||
|
||||
doAssert isAlphaNumeric('3')
|
||||
doAssert isAlphaNumeric('R')
|
||||
@@ -2163,13 +2317,13 @@ when isMainModule:
|
||||
doAssert(not isDigit("12.33"))
|
||||
doAssert(not isDigit("A45b"))
|
||||
|
||||
doAssert isSpace('\t')
|
||||
doAssert isSpace('\l')
|
||||
doAssert(not isSpace('A'))
|
||||
doAssert isSpaceAscii('\t')
|
||||
doAssert isSpaceAscii('\l')
|
||||
doAssert(not isSpaceAscii('A'))
|
||||
|
||||
doAssert isSpace("\t\l \v\r\f")
|
||||
doAssert isSpace(" ")
|
||||
doAssert(not isSpace("ABc \td"))
|
||||
doAssert isSpaceAscii("\t\l \v\r\f")
|
||||
doAssert isSpaceAscii(" ")
|
||||
doAssert(not isSpaceAscii("ABc \td"))
|
||||
|
||||
doAssert(isNilOrEmpty(""))
|
||||
doAssert(isNilOrEmpty(nil))
|
||||
@@ -2182,24 +2336,24 @@ when isMainModule:
|
||||
doAssert(isNilOrWhitespace("\t\l \v\r\f"))
|
||||
doAssert(not isNilOrWhitespace("ABc \td"))
|
||||
|
||||
doAssert isLower('a')
|
||||
doAssert isLower('z')
|
||||
doAssert(not isLower('A'))
|
||||
doAssert(not isLower('5'))
|
||||
doAssert(not isLower('&'))
|
||||
doAssert isLowerAscii('a')
|
||||
doAssert isLowerAscii('z')
|
||||
doAssert(not isLowerAscii('A'))
|
||||
doAssert(not isLowerAscii('5'))
|
||||
doAssert(not isLowerAscii('&'))
|
||||
|
||||
doAssert isLower("abcd")
|
||||
doAssert(not isLower("abCD"))
|
||||
doAssert(not isLower("33aa"))
|
||||
doAssert isLowerAscii("abcd")
|
||||
doAssert(not isLowerAscii("abCD"))
|
||||
doAssert(not isLowerAscii("33aa"))
|
||||
|
||||
doAssert isUpper('A')
|
||||
doAssert(not isUpper('b'))
|
||||
doAssert(not isUpper('5'))
|
||||
doAssert(not isUpper('%'))
|
||||
doAssert isUpperAscii('A')
|
||||
doAssert(not isUpperAscii('b'))
|
||||
doAssert(not isUpperAscii('5'))
|
||||
doAssert(not isUpperAscii('%'))
|
||||
|
||||
doAssert isUpper("ABC")
|
||||
doAssert(not isUpper("AAcc"))
|
||||
doAssert(not isUpper("A#$"))
|
||||
doAssert isUpperAscii("ABC")
|
||||
doAssert(not isUpperAscii("AAcc"))
|
||||
doAssert(not isUpperAscii("A#$"))
|
||||
|
||||
doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
|
||||
doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
|
||||
@@ -2244,11 +2398,14 @@ bar
|
||||
bar
|
||||
""".unindent() == "foo\nfoo\nbar\n"
|
||||
|
||||
let s = " this is an example "
|
||||
doAssert s.split() == @["this", "is", "an", "example"]
|
||||
doAssert s.split(maxsplit=4) == @["this", "is", "an", "example"]
|
||||
doAssert s.split(' ', maxsplit=4) == @["", "this", "", "", "is an example "]
|
||||
doAssert s.split(" ", maxsplit=4) == @["", "this", "", "", "is an example "]
|
||||
let s = " this is an example "
|
||||
let s2 = ":this;is;an:example;;"
|
||||
|
||||
doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "]
|
||||
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
|
||||
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
|
||||
|
||||
block: # formatEng tests
|
||||
doAssert formatEng(0, 2, trim=false) == "0.00"
|
||||
|
||||
@@ -1369,6 +1369,64 @@ proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
(c >= 0x20d0 and c <= 0x20ff) or
|
||||
(c >= 0xfe20 and c <= 0xfe2f))
|
||||
|
||||
template runeCheck(s, runeProc) =
|
||||
## Common code for rune.isLower, rune.isUpper, etc
|
||||
result = if len(s) == 0: false else: true
|
||||
|
||||
var
|
||||
i = 0
|
||||
rune: Rune
|
||||
|
||||
while i < len(s) and result:
|
||||
fastRuneAt(s, i, rune, doInc=true)
|
||||
result = runeProc(rune) and result
|
||||
|
||||
proc isUpper*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1Str".} =
|
||||
## Returns true iff `s` contains all upper case unicode characters.
|
||||
runeCheck(s, isUpper)
|
||||
|
||||
proc isLower*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1Str".} =
|
||||
## Returns true iff `s` contains all lower case unicode characters.
|
||||
runeCheck(s, isLower)
|
||||
|
||||
proc isAlpha*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1Str".} =
|
||||
## Returns true iff `s` contains all alphabetic unicode characters.
|
||||
runeCheck(s, isAlpha)
|
||||
|
||||
proc isSpace*(s: string): bool {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1Str".} =
|
||||
## Returns true iff `s` contains all whitespace unicode characters.
|
||||
runeCheck(s, isWhiteSpace)
|
||||
|
||||
template convertRune(s, runeProc) =
|
||||
## Convert runes in `s` using `runeProc` as the converter.
|
||||
result = newString(len(s))
|
||||
|
||||
var
|
||||
i = 0
|
||||
lastIndex = 0
|
||||
rune: Rune
|
||||
|
||||
while i < len(s):
|
||||
lastIndex = i
|
||||
fastRuneAt(s, i, rune, doInc=true)
|
||||
rune = runeProc(rune)
|
||||
|
||||
rune.fastToUTF8Copy(result, lastIndex)
|
||||
|
||||
proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1Str".} =
|
||||
## Converts `s` into upper-case unicode characters.
|
||||
convertRune(s, toUpper)
|
||||
|
||||
proc toLower*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1Str".} =
|
||||
## Converts `s` into lower-case unicode characters.
|
||||
convertRune(s, toLower)
|
||||
|
||||
proc swapCase*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1".} =
|
||||
## Swaps the case of unicode characters in `s`
|
||||
@@ -1395,6 +1453,20 @@ proc swapCase*(s: string): string {.noSideEffect, procvar,
|
||||
|
||||
rune.fastToUTF8Copy(result, lastIndex)
|
||||
|
||||
proc capitalize*(s: string): string {.noSideEffect, procvar,
|
||||
rtl, extern: "nuc$1".} =
|
||||
## Converts the first character of `s` into an upper-case unicode character.
|
||||
if len(s) == 0:
|
||||
return s
|
||||
|
||||
var
|
||||
rune: Rune
|
||||
i = 0
|
||||
|
||||
fastRuneAt(s, i, rune, doInc=true)
|
||||
|
||||
result = $toUpper(rune) & substr(s, i)
|
||||
|
||||
proc translate*(s: string, replacements: proc(key: string): string): string {.
|
||||
rtl, extern: "nuc$1".} =
|
||||
## Translates words in a string using the `replacements` proc to substitute
|
||||
@@ -1618,6 +1690,10 @@ when isMainModule:
|
||||
doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
|
||||
doAssert title("") == ""
|
||||
|
||||
doAssert capitalize("βeta") == "Βeta"
|
||||
doAssert capitalize("foo") == "Foo"
|
||||
doAssert capitalize("") == ""
|
||||
|
||||
doAssert isTitle("Foo")
|
||||
doAssert(not isTitle("Foo bar"))
|
||||
doAssert(not isTitle("αlpha Βeta"))
|
||||
@@ -1630,6 +1706,64 @@ when isMainModule:
|
||||
doAssert swapCase("a✓B") == "A✓b"
|
||||
doAssert swapCase("") == ""
|
||||
|
||||
doAssert isAlpha("r")
|
||||
doAssert isAlpha("α")
|
||||
doAssert(not isAlpha("$"))
|
||||
doAssert(not isAlpha(""))
|
||||
|
||||
doAssert isAlpha("Βeta")
|
||||
doAssert isAlpha("Args")
|
||||
doAssert(not isAlpha("$Foo✓"))
|
||||
|
||||
doAssert isSpace("\t")
|
||||
doAssert isSpace("\l")
|
||||
doAssert(not isSpace("Β"))
|
||||
doAssert(not isSpace("Βeta"))
|
||||
|
||||
doAssert isSpace("\t\l \v\r\f")
|
||||
doAssert isSpace(" ")
|
||||
doAssert(not isSpace(""))
|
||||
doAssert(not isSpace("ΑΓc \td"))
|
||||
|
||||
doAssert isLower("a")
|
||||
doAssert isLower("γ")
|
||||
doAssert(not isLower("Γ"))
|
||||
doAssert(not isLower("4"))
|
||||
doAssert(not isLower(""))
|
||||
|
||||
doAssert isLower("abcdγ")
|
||||
doAssert(not isLower("abCDΓ"))
|
||||
doAssert(not isLower("33aaΓ"))
|
||||
|
||||
doAssert isUpper("Γ")
|
||||
doAssert(not isUpper("b"))
|
||||
doAssert(not isUpper("α"))
|
||||
doAssert(not isUpper("✓"))
|
||||
doAssert(not isUpper(""))
|
||||
|
||||
doAssert isUpper("ΑΒΓ")
|
||||
doAssert(not isUpper("AAccβ"))
|
||||
doAssert(not isUpper("A#$β"))
|
||||
|
||||
doAssert toUpper("Γ") == "Γ"
|
||||
doAssert toUpper("b") == "B"
|
||||
doAssert toUpper("α") == "Α"
|
||||
doAssert toUpper("✓") == "✓"
|
||||
doAssert toUpper("") == ""
|
||||
|
||||
doAssert toUpper("ΑΒΓ") == "ΑΒΓ"
|
||||
doAssert toUpper("AAccβ") == "AACCΒ"
|
||||
doAssert toUpper("A✓$β") == "A✓$Β"
|
||||
|
||||
doAssert toLower("a") == "a"
|
||||
doAssert toLower("γ") == "γ"
|
||||
doAssert toLower("Γ") == "γ"
|
||||
doAssert toLower("4") == "4"
|
||||
doAssert toLower("") == ""
|
||||
|
||||
doAssert toLower("abcdγ") == "abcdγ"
|
||||
doAssert toLower("abCDΓ") == "abcdγ"
|
||||
doAssert toLower("33aaΓ") == "33aaγ"
|
||||
|
||||
doAssert reversed("Reverse this!") == "!siht esreveR"
|
||||
doAssert reversed("先秦兩漢") == "漢兩秦先"
|
||||
|
||||
@@ -3633,6 +3633,27 @@ proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
|
||||
elif x.isNil or y.isNil: result = false
|
||||
else: result = strcmp(x, y) == 0
|
||||
|
||||
template closureScope*(body: untyped): stmt =
|
||||
## Useful when creating a closure in a loop to capture local loop variables by
|
||||
## their current iteration values. Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var myClosure : proc()
|
||||
## # without closureScope:
|
||||
## for i in 0 .. 5:
|
||||
## let j = i
|
||||
## if j == 3:
|
||||
## myClosure = proc() = echo j
|
||||
## myClosure() # outputs 5. `j` is changed after closure creation
|
||||
## # with closureScope:
|
||||
## for i in 0 .. 5:
|
||||
## closureScope: # Everything in this scope is locked after closure creation
|
||||
## let j = i
|
||||
## if j == 3:
|
||||
## myClosure = proc() = echo j
|
||||
## myClosure() # outputs 3
|
||||
(proc() = body)()
|
||||
|
||||
{.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
|
||||
|
||||
when defined(nimconfig):
|
||||
|
||||
@@ -124,7 +124,9 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
|
||||
copyMem(dest, src, mt.size)
|
||||
|
||||
proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
|
||||
GC_disable()
|
||||
genericDeepCopyAux(dest, src, mt)
|
||||
GC_enable()
|
||||
|
||||
proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
|
||||
# also invoked for 'string'
|
||||
|
||||
@@ -39,6 +39,9 @@ proc getCurrentDir(): string = builtin
|
||||
proc rawExec(cmd: string): int {.tags: [ExecIOEffect], raises: [OSError].} =
|
||||
builtin
|
||||
|
||||
proc warningImpl(arg, orig: string) = discard
|
||||
proc hintImpl(arg, orig: string) = discard
|
||||
|
||||
proc paramStr*(i: int): string =
|
||||
## Retrieves the ``i``'th command line parameter.
|
||||
builtin
|
||||
@@ -52,6 +55,16 @@ proc switch*(key: string, val="") =
|
||||
## example ``switch("checks", "on")``.
|
||||
builtin
|
||||
|
||||
proc warning*(name: string; val: bool) =
|
||||
## Disables or enables a specific warning.
|
||||
let v = if val: "on" else: "off"
|
||||
warningImpl(name & "]:" & v, "warning[" & name & "]:" & v)
|
||||
|
||||
proc hint*(name: string; val: bool) =
|
||||
## Disables or enables a specific hint.
|
||||
let v = if val: "on" else: "off"
|
||||
hintImpl(name & "]:" & v, "hint[" & name & "]:" & v)
|
||||
|
||||
proc getCommand*(): string =
|
||||
## Gets the Nim command that the compiler has been invoked with, for example
|
||||
## "c", "js", "build", "help".
|
||||
|
||||
@@ -150,8 +150,9 @@ elif defined(windows):
|
||||
#VirtualFree(p, size, MEM_DECOMMIT)
|
||||
|
||||
elif hostOS == "standalone":
|
||||
const StandaloneHeapSize {.intdefine.}: int = 1024 * PageSize
|
||||
var
|
||||
theHeap: array[1024*PageSize, float64] # 'float64' for alignment
|
||||
theHeap: array[StandaloneHeapSize, float64] # 'float64' for alignment
|
||||
bumpPointer = cast[int](addr theHeap)
|
||||
|
||||
proc osAllocPages(size: int): pointer {.inline.} =
|
||||
|
||||
@@ -73,23 +73,20 @@ proc reprChar(x: char): string {.compilerRtl.} =
|
||||
add result, "\'"
|
||||
|
||||
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
|
||||
# we read an 'int' but this may have been too large, so mask the other bits:
|
||||
let b = (sizeof(int)-typ.size)*8 # bits
|
||||
let m = 1 shl (b-1) # mask
|
||||
var o = e and ((1 shl b)-1) # clear upper bits
|
||||
o = (o xor m) - m # sign extend
|
||||
# XXX we need a proper narrowing based on signedness here
|
||||
#e and ((1 shl (typ.size*8)) - 1)
|
||||
## Return string representation for enumeration values
|
||||
var n = typ.node
|
||||
if ntfEnumHole notin typ.flags:
|
||||
if o <% typ.node.len:
|
||||
return $typ.node.sons[o].name
|
||||
let o = e - n.sons[0].offset
|
||||
if o >= 0 and o <% typ.node.len:
|
||||
return $n.sons[o].name
|
||||
else:
|
||||
# ugh we need a slow linear search:
|
||||
var n = typ.node
|
||||
var s = n.sons
|
||||
for i in 0 .. n.len-1:
|
||||
if s[i].offset == o: return $s[i].name
|
||||
result = $o & " (invalid data!)"
|
||||
if s[i].offset == e:
|
||||
return $s[i].name
|
||||
|
||||
result = $e & " (invalid data!)"
|
||||
|
||||
type
|
||||
PByteArray = ptr array[0.. 0xffff, int8]
|
||||
|
||||
2154
lib/upcoming/asyncdispatch.nim
Normal file
2154
lib/upcoming/asyncdispatch.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +1,4 @@
|
||||
discard """
|
||||
output: ""
|
||||
"""
|
||||
import "../template/utemplates", "../closure/uclosures"
|
||||
|
||||
9
tests/async/tgeneric_async.nim
Normal file
9
tests/async/tgeneric_async.nim
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
import asyncdispatch
|
||||
|
||||
when true:
|
||||
# bug #2377
|
||||
proc test[T](v: T) {.async.} =
|
||||
echo $v
|
||||
|
||||
asyncCheck test[int](1)
|
||||
@@ -58,6 +58,7 @@ when not defined(windows):
|
||||
discard posix.connect(client_socket, aiList.ai_addr,
|
||||
aiList.ai_addrlen.Socklen)
|
||||
dealloc(aiList)
|
||||
discard selector.select(100)
|
||||
var rc1 = selector.select(100)
|
||||
assert(len(rc1) == 2)
|
||||
|
||||
@@ -124,7 +125,7 @@ when not defined(windows):
|
||||
|
||||
proc event_notification_test(): bool =
|
||||
var selector = newSelector[int]()
|
||||
var event = newEvent()
|
||||
var event = newSelectEvent()
|
||||
selector.registerEvent(event, 1)
|
||||
selector.flush()
|
||||
event.setEvent()
|
||||
@@ -235,7 +236,7 @@ when not defined(windows):
|
||||
thr: array [0..7, Thread[SelectEvent]]
|
||||
var selector = newSelector[int]()
|
||||
var sock = newNativeSocket()
|
||||
var event = newEvent()
|
||||
var event = newSelectEvent()
|
||||
for i in 0..high(thr):
|
||||
createThread(thr[i], event_wait_thread, event)
|
||||
selector.registerHandle(sock, {Event.Read}, 1)
|
||||
@@ -358,7 +359,7 @@ else:
|
||||
|
||||
proc event_notification_test(): bool =
|
||||
var selector = newSelector[int]()
|
||||
var event = newEvent()
|
||||
var event = newSelectEvent()
|
||||
selector.registerEvent(event, 1)
|
||||
selector.flush()
|
||||
event.setEvent()
|
||||
@@ -391,7 +392,7 @@ else:
|
||||
|
||||
proc mt_event_test(): bool =
|
||||
var thr: array [0..7, Thread[SelectEvent]]
|
||||
var event = newEvent()
|
||||
var event = newSelectEvent()
|
||||
for i in 0..high(thr):
|
||||
createThread(thr[i], event_wait_thread, event)
|
||||
event.setEvent()
|
||||
|
||||
23
tests/async/treturn_await.nim
Normal file
23
tests/async/treturn_await.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
# bug #4371
|
||||
|
||||
import strutils, asyncdispatch, asynchttpserver
|
||||
|
||||
type
|
||||
List[A] = ref object
|
||||
value: A
|
||||
next: List[A]
|
||||
StrPair* = tuple[k, v: string]
|
||||
Context* = object
|
||||
position*: int
|
||||
accept*: bool
|
||||
headers*: List[StrPair]
|
||||
Handler* = proc(req: ref Request, ctx: Context): Future[Context]
|
||||
|
||||
proc logging*(handler: Handler): auto =
|
||||
proc h(req: ref Request, ctx: Context): Future[Context] {.async.} =
|
||||
let ret = handler(req, ctx)
|
||||
debugEcho "$3 $1 $2".format(req.reqMethod, req.url.path, req.hostname)
|
||||
return await ret
|
||||
|
||||
return h
|
||||
@@ -1,12 +1,23 @@
|
||||
# This test is included from within tunittests
|
||||
import unittest
|
||||
|
||||
test "loop variables are captured by copy":
|
||||
test "loop variables are captured by ref":
|
||||
var funcs: seq[proc (): int {.closure.}] = @[]
|
||||
|
||||
for i in 0..10:
|
||||
let ii = i
|
||||
funcs.add do -> int: return ii * ii
|
||||
|
||||
check funcs[0]() == 100
|
||||
check funcs[3]() == 100
|
||||
|
||||
test "loop variables in closureScope are captured by copy":
|
||||
var funcs: seq[proc (): int {.closure.}] = @[]
|
||||
|
||||
for i in 0..10:
|
||||
closureScope:
|
||||
let ii = i
|
||||
funcs.add do -> int: return ii * ii
|
||||
|
||||
check funcs[0]() == 0
|
||||
check funcs[3]() == 9
|
||||
|
||||
|
||||
28
tests/generics/tforward_generic.nim
Normal file
28
tests/generics/tforward_generic.nim
Normal file
@@ -0,0 +1,28 @@
|
||||
discard """
|
||||
output: '''b()
|
||||
720 120.0'''
|
||||
"""
|
||||
|
||||
# bug #3055
|
||||
proc b(t: int | string)
|
||||
proc a(t: int) = b(t)
|
||||
proc b(t: int | string) = echo "b()"
|
||||
a(1)
|
||||
|
||||
# test recursive generics still work:
|
||||
proc fac[T](x: T): T =
|
||||
if x == 0: return 1
|
||||
else: return fac(x-1)*x
|
||||
|
||||
echo fac(6), " ", fac(5.0)
|
||||
|
||||
when false:
|
||||
# This still doesn't work...
|
||||
# test recursive generic with forwarding:
|
||||
proc fac2[T](x: T): T
|
||||
|
||||
echo fac2(6), " ", fac2(5.0)
|
||||
|
||||
proc fac2[T](x: T): T =
|
||||
if x == 0: return 1
|
||||
else: return fac2(x-1)*x
|
||||
42
tests/macros/tcomplexecho.nim
Normal file
42
tests/macros/tcomplexecho.nim
Normal file
@@ -0,0 +1,42 @@
|
||||
discard """
|
||||
output: '''3
|
||||
OK
|
||||
56
|
||||
123
|
||||
56
|
||||
61'''
|
||||
"""
|
||||
|
||||
import macros
|
||||
|
||||
# Bug from the forum
|
||||
macro addEcho1(s: untyped): stmt =
|
||||
s.body.add(newCall("echo", newStrLitNode("OK")))
|
||||
result = s
|
||||
|
||||
proc f1() {.addEcho1.} =
|
||||
let i = 1+2
|
||||
echo i
|
||||
|
||||
f1()
|
||||
|
||||
# bug #537
|
||||
proc test(): seq[NimNode] {.compiletime.} =
|
||||
result = @[]
|
||||
result.add parseExpr("echo 56")
|
||||
result.add parseExpr("echo 123")
|
||||
result.add parseExpr("echo 56")
|
||||
|
||||
proc foo(): seq[NimNode] {.compiletime.} =
|
||||
result = @[]
|
||||
result.add test()
|
||||
result.add parseExpr("echo(5+56)")
|
||||
|
||||
macro bar(): stmt =
|
||||
result = newNimNode(nnkStmtList)
|
||||
let x = foo()
|
||||
for xx in x:
|
||||
result.add xx
|
||||
echo treeRepr(result)
|
||||
|
||||
bar()
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
output: "empty"
|
||||
output: "void"
|
||||
"""
|
||||
|
||||
# bug #898
|
||||
|
||||
21
tests/metatype/tvoid_must_not_match.nim
Normal file
21
tests/metatype/tvoid_must_not_match.nim
Normal file
@@ -0,0 +1,21 @@
|
||||
discard """
|
||||
errormsg: "type mismatch: got (Future[system.int], void)"
|
||||
line: 20
|
||||
"""
|
||||
|
||||
type
|
||||
Future[T] = object
|
||||
value: T
|
||||
|
||||
proc complete[T](x: T) =
|
||||
echo "completed"
|
||||
let y = x
|
||||
|
||||
|
||||
proc complete*[T](future: var Future[T], val: T) =
|
||||
future.value = val
|
||||
|
||||
var a: Future[int]
|
||||
|
||||
complete(a):
|
||||
echo "yielding void"
|
||||
9
tests/modules/texplicit_system_import.nim
Normal file
9
tests/modules/texplicit_system_import.nim
Normal file
@@ -0,0 +1,9 @@
|
||||
##.
|
||||
import system except `+`
|
||||
discard """
|
||||
errormsg: "undeclared identifier: '+'"
|
||||
line: 9
|
||||
"""
|
||||
# Testament requires that the initial """ occurs before the 40th byte
|
||||
# in the file. No kidding...
|
||||
echo 4+5
|
||||
@@ -8,6 +8,9 @@ import ospaths
|
||||
|
||||
--forceBuild
|
||||
|
||||
warning("uninit", off)
|
||||
hint("processing", off)
|
||||
|
||||
task listDirs, "lists every subdirectory":
|
||||
for x in listDirs("."):
|
||||
echo "DIR ", x
|
||||
|
||||
@@ -9,7 +9,7 @@ for w in split("|abc|xy|z", {'|'}):
|
||||
s.add("#")
|
||||
s.add(w)
|
||||
|
||||
if s == "#abc#xy#z":
|
||||
if s == "##abc#xy#z":
|
||||
echo "true"
|
||||
else:
|
||||
echo "false"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
discard """
|
||||
line: 16
|
||||
errormsg: "type mismatch: got (empty)"
|
||||
errormsg: "type mismatch: got (void)"
|
||||
"""
|
||||
|
||||
# bug #950
|
||||
|
||||
16
tests/vm/tvmmisc.nim
Normal file
16
tests/vm/tvmmisc.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
# bug #4462
|
||||
import macros
|
||||
|
||||
proc foo(t: typedesc) {.compileTime.} =
|
||||
echo getType(t).treeRepr
|
||||
|
||||
static:
|
||||
foo(int)
|
||||
|
||||
# #4412
|
||||
proc default[T](t: typedesc[T]): T {.inline.} = discard
|
||||
|
||||
static:
|
||||
var x = default(type(0))
|
||||
@@ -3,7 +3,35 @@
|
||||
# result = "#! /bin/sh\n# Generated by niminst\n"
|
||||
# var proj = c.name.toLower
|
||||
|
||||
set -e
|
||||
## Current directory you start script from
|
||||
BASE_DIR=$(pwd)
|
||||
|
||||
## The following one-liner takes directory path which contains install script.
|
||||
## `command -v -- "$0"` takes path if script sourced from interactive shell
|
||||
## `dirname` returns relative directory path to install script
|
||||
## `cd -P` dive into directory to use `pwd`
|
||||
## `pwd -P` prints full path to install script directory path
|
||||
## -P option allows to use symlinks in path
|
||||
## Good explanation can be found here:
|
||||
## http://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
|
||||
NIM_DIR=$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)
|
||||
|
||||
go_back() {
|
||||
cd $BASE_DIR
|
||||
}
|
||||
|
||||
## Go to base dir on exit
|
||||
trap go_back EXIT
|
||||
|
||||
install_error() {
|
||||
echo "Nim installation failed!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
## Exit if any command failed
|
||||
trap install_error ERR ## `set -e` alternative
|
||||
|
||||
cd $NIM_DIR
|
||||
|
||||
if [ $# -eq 1 ] ; then
|
||||
# if c.cat[fcUnixBin].len > 0:
|
||||
|
||||
@@ -13,6 +13,12 @@ Changes affecting backwards compatibility
|
||||
- De-deprecated ``re.nim`` because we have too much code using it
|
||||
and it got the basic API right.
|
||||
|
||||
- ``split`` with ``set[char]`` as a delimiter in ``strutils.nim``
|
||||
no longer strips and splits characters out of the target string
|
||||
by the entire set of characters. Instead, it now behaves in a
|
||||
similar fashion to ``split`` with ``string`` and ``char``
|
||||
delimiters.
|
||||
|
||||
Library Additions
|
||||
-----------------
|
||||
|
||||
@@ -23,8 +29,10 @@ Library Additions
|
||||
- Added ``center`` and ``rsplit`` to ``strutils.nim`` to
|
||||
provide similar Python functionality for Nim's strings.
|
||||
|
||||
- Added ``isTitle``, ``title``, and ``swapCase`` to ``unicode.nim`` to
|
||||
provide unicode aware string case manipulation.
|
||||
- Added ``isTitle``, ``title``, ``swapCase``, ``isUpper``, ``toUpper``,
|
||||
``isLower``, ``toLower``, ``isAlpha``, ``isSpace``, and ``capitalize``
|
||||
to ``unicode.nim`` to provide unicode aware case manipulation and case
|
||||
testing.
|
||||
|
||||
- Added a new module ``lib/pure/strmisc.nim`` to hold uncommon string
|
||||
operations. Currently contains ``partition``, ``rpartition``
|
||||
@@ -38,8 +46,17 @@ Library Additions
|
||||
Compiler Additions
|
||||
------------------
|
||||
|
||||
- The ``-d/--define`` flag can now optionally take a value to be used
|
||||
by code at compile time.
|
||||
|
||||
Language Additions
|
||||
------------------
|
||||
|
||||
- Added ``{.intdefine.}`` and ``{.strdefine.}`` macros to make use of
|
||||
(optional) compile time defines.
|
||||
- If the first statement is an ``import system`` statement then ``system``
|
||||
is not imported implicitly anymore. This allows for code like
|
||||
``import system except echo`` or ``from system import nil``.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
Reference in New Issue
Block a user