Merge branch 'devel' into bugfix-2858-testament-sources-system-wide

This commit is contained in:
Oscar Campbell 2015-06-04 22:43:18 +02:00
commit cdf631c508
130 changed files with 5899 additions and 5730 deletions

View file

@ -842,7 +842,7 @@ type
data*: TIdNodePairSeq data*: TIdNodePairSeq
TNodePair* = object TNodePair* = object
h*: THash # because it is expensive to compute! h*: Hash # because it is expensive to compute!
key*: PNode key*: PNode
val*: int val*: int

View file

@ -14,7 +14,7 @@
import import
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: RootRef): THash proc hashNode*(p: RootRef): Hash
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the # Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes. # YAML code generator and it is invaluable for debugging purposes.
@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym
type type
TTabIter*{.final.} = object # consider all fields here private TTabIter*{.final.} = object # consider all fields here private
h*: THash # current hash h*: Hash # current hash
proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
type type
TIdentIter*{.final.} = object # iterator over all syms with same identifier TIdentIter*{.final.} = object # iterator over all syms with same identifier
h*: THash # current hash h*: Hash # current hash
name*: PIdent name*: PIdent
@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
proc lookupInRecord*(n: PNode, field: PIdent): PSym proc lookupInRecord*(n: PNode, field: PIdent): PSym
proc getModule*(s: PSym): PSym proc getModule*(s: PSym): PSym
proc mustRehash*(length, counter: int): bool proc mustRehash*(length, counter: int): bool
proc nextTry*(h, maxHash: THash): THash {.inline.} proc nextTry*(h, maxHash: Hash): Hash {.inline.}
# ------------- table[int, int] --------------------------------------------- # ------------- table[int, int] ---------------------------------------------
const const
@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
else: internalError(list.info, "getSymFromList") else: internalError(list.info, "getSymFromList")
result = nil result = nil
proc hashNode(p: RootRef): THash = proc hashNode(p: RootRef): Hash =
result = hash(cast[pointer](p)) result = hash(cast[pointer](p))
proc mustRehash(length, counter: int): bool = proc mustRehash(length, counter: int): bool =
@ -466,7 +466,7 @@ proc debug(n: PNode) =
const const
EmptySeq = @[] EmptySeq = @[]
proc nextTry(h, maxHash: THash): THash = proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times # For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on # generates each int in range(maxHash) exactly once (see any text on
@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash =
proc objectSetContains(t: TObjectSet, obj: RootRef): bool = proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
# returns true whether n is in t # returns true whether n is in t
var h: THash = hashNode(obj) and high(t.data) # start with real hash value var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
while t.data[h] != nil: while t.data[h] != nil:
if t.data[h] == obj: if t.data[h] == obj:
return true return true
@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
result = false result = false
proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) = proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
var h: THash = hashNode(obj) and high(data) var h: Hash = hashNode(obj) and high(data)
while data[h] != nil: while data[h] != nil:
assert(data[h] != obj) assert(data[h] != obj)
h = nextTry(h, high(data)) h = nextTry(h, high(data))
@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
# returns true if obj is already in the string table: # returns true if obj is already in the string table:
var h: THash = hashNode(obj) and high(t.data) var h: Hash = hashNode(obj) and high(t.data)
while true: while true:
var it = t.data[h] var it = t.data[h]
if it == nil: break if it == nil: break
@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
result = false result = false
proc tableRawGet(t: TTable, key: RootRef): int = proc tableRawGet(t: TTable, key: RootRef): int =
var h: THash = hashNode(key) and high(t.data) # start with real hash value var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key == key: if t.data[h].key == key:
return h return h
@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int =
proc tableSearch(t: TTable, key, closure: RootRef, proc tableSearch(t: TTable, key, closure: RootRef,
comparator: TCmpProc): RootRef = comparator: TCmpProc): RootRef =
var h: THash = hashNode(key) and high(t.data) # start with real hash value var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key == key: if t.data[h].key == key:
if comparator(t.data[h].val, closure): if comparator(t.data[h].val, closure):
@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef =
else: result = nil else: result = nil
proc tableRawInsert(data: var TPairSeq, key, val: RootRef) = proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
var h: THash = hashNode(key) and high(data) var h: Hash = hashNode(key) and high(data)
while data[h].key != nil: while data[h].key != nil:
assert(data[h].key != key) assert(data[h].key != key)
h = nextTry(h, high(data)) h = nextTry(h, high(data))
@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) =
inc(t.counter) inc(t.counter)
proc strTableContains(t: TStrTable, n: PSym): bool = proc strTableContains(t: TStrTable, n: PSym): bool =
var h: THash = n.name.h and high(t.data) # start with real hash value var h: Hash = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil: while t.data[h] != nil:
if (t.data[h] == n): if (t.data[h] == n):
return true return true
@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
result = false result = false
proc strTableRawInsert(data: var TSymSeq, n: PSym) = proc strTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data) var h: Hash = n.name.h and high(data)
if sfImmediate notin n.flags: if sfImmediate notin n.flags:
# fast path: # fast path:
while data[h] != nil: while data[h] != nil:
@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) = proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h assert prevSym.name.h == newSym.name.h
var h: THash = prevSym.name.h and high(data) var h: Hash = prevSym.name.h and high(data)
while data[h] != nil: while data[h] != nil:
if data[h] == prevSym: if data[h] == prevSym:
data[h] = newSym data[h] = newSym
@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
# It is essential that `n` is written nevertheless! # It is essential that `n` is written nevertheless!
# This way the newest redefinition is picked by the semantic analyses! # This way the newest redefinition is picked by the semantic analyses!
assert n.name != nil assert n.name != nil
var h: THash = n.name.h and high(t.data) var h: Hash = n.name.h and high(t.data)
var replaceSlot = -1 var replaceSlot = -1
while true: while true:
var it = t.data[h] var it = t.data[h]
@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
result = false result = false
proc strTableGet(t: TStrTable, name: PIdent): PSym = proc strTableGet(t: TStrTable, name: PIdent): PSym =
var h: THash = name.h and high(t.data) var h: Hash = name.h and high(t.data)
while true: while true:
result = t.data[h] result = t.data[h]
if result == nil: break if result == nil: break
@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
excluding: IntSet): PSym = excluding: IntSet): PSym =
var h: THash = ti.h and high(tab.data) var h: Hash = ti.h and high(tab.data)
var start = h var start = h
result = tab.data[h] result = tab.data[h]
while result != nil: while result != nil:
@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool =
result = false result = false
proc idTableRawGet(t: TIdTable, key: int): int = proc idTableRawGet(t: TIdTable, key: int): int =
var h: THash var h: Hash
h = key and high(t.data) # start with real hash value h = key and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key.id == key: if t.data[h].key.id == key:
@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
yield (t.data[i].key.id, t.data[i].val) yield (t.data[i].key.id, t.data[i].val)
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) = proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
var h: THash var h: Hash
h = key.id and high(data) h = key.id and high(data)
while data[h].key != nil: while data[h].key != nil:
assert(data[h].key.id != key.id) assert(data[h].key.id != key.id)
@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: THash var h: Hash
h = key.id and high(t.data) # start with real hash value h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key.id == key.id: if t.data[h].key.id == key.id:
@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
result = idNodeTableGet(t, key) result = idNodeTableGet(t, key)
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: THash var h: Hash
h = key.id and high(data) h = key.id and high(data)
while data[h].key != nil: while data[h].key != nil:
assert(data[h].key.id != key.id) assert(data[h].key.id != key.id)
@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) =
for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
proc iiTableRawGet(t: TIITable, key: int): int = proc iiTableRawGet(t: TIITable, key: int): int =
var h: THash var h: Hash
h = key and high(t.data) # start with real hash value h = key and high(t.data) # start with real hash value
while t.data[h].key != InvalidKey: while t.data[h].key != InvalidKey:
if t.data[h].key == key: return h if t.data[h].key == key: return h
@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int =
else: result = InvalidKey else: result = InvalidKey
proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) = proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) =
var h: THash var h: Hash
h = key and high(data) h = key and high(data)
while data[h].key != InvalidKey: while data[h].key != InvalidKey:
assert(data[h].key != key) assert(data[h].key != key)

View file

@ -18,7 +18,7 @@ import
type type
TSections = array[TSymKind, Rope] TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.TRstGenerator TDocumentor = object of rstgen.RstGenerator
modDesc: Rope # module description modDesc: Rope # module description
id: int # for generating IDs id: int # for generating IDs
toc, section: TSections toc, section: TSections
@ -29,7 +29,7 @@ type
PDoc* = ref TDocumentor ## Alias to type less. PDoc* = ref TDocumentor ## Alias to type less.
proc compilerMsgHandler(filename: string, line, col: int, proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.TMsgKind, arg: string) {.procvar.} = msgKind: rst.MsgKind, arg: string) {.procvar.} =
# translate msg kind: # translate msg kind:
var k: msgs.TMsgKind var k: msgs.TMsgKind
case msgKind case msgKind
@ -53,7 +53,7 @@ proc docgenFindFile(s: string): string {.procvar.} =
proc parseRst(text, filename: string, proc parseRst(text, filename: string,
line, column: int, hasToc: var bool, line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode = rstOptions: RstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions, result = rstParse(text, filename, line, column, hasToc, rstOptions,
docgenFindFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)

View file

@ -12,7 +12,7 @@
# id. This module is essential for the compiler's performance. # id. This module is essential for the compiler's performance.
import import
hashes, strutils hashes, strutils, etcpriv
type type
TIdObj* = object of RootObj TIdObj* = object of RootObj
@ -23,7 +23,7 @@ type
TIdent*{.acyclic.} = object of TIdObj TIdent*{.acyclic.} = object of TIdObj
s*: string s*: string
next*: PIdent # for hash-table chaining next*: PIdent # for hash-table chaining
h*: THash # hash value of s h*: Hash # hash value of s
var firstCharIsCS*: bool = true var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent] var buckets*: array[0..4096 * 2 - 1, PIdent]
@ -37,6 +37,8 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
while j < blen: while j < blen:
while a[i] == '_': inc(i) while a[i] == '_': inc(i)
while b[j] == '_': inc(j) while b[j] == '_': inc(j)
while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth)
while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth)
# tolower inlined: # tolower inlined:
var aa = a[i] var aa = a[i]
var bb = b[j] var bb = b[j]
@ -65,7 +67,7 @@ proc cmpExact(a, b: cstring, blen: int): int =
var wordCounter = 1 var wordCounter = 1
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent = proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
var idx = h and high(buckets) var idx = h and high(buckets)
result = buckets[idx] result = buckets[idx]
var last: PIdent = nil var last: PIdent = nil
@ -99,7 +101,7 @@ proc getIdent*(identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier), result = getIdent(cstring(identifier), len(identifier),
hashIgnoreStyle(identifier)) hashIgnoreStyle(identifier))
proc getIdent*(identifier: string, h: THash): PIdent = proc getIdent*(identifier: string, h: Hash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h) result = getIdent(cstring(identifier), len(identifier), h)
proc identEq*(id: PIdent, name: string): bool = proc identEq*(id: PIdent, name: string): bool =

View file

@ -986,6 +986,15 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')') else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')')
else: internalError(n.sons[0].info, "genAddr") else: internalError(n.sons[0].info, "genAddr")
proc genProcForSymIfNeeded(p: PProc, s: PSym) =
if not p.g.generatedSyms.containsOrIncl(s.id):
let newp = genProc(p, s)
var owner = p
while owner != nil and owner.prc != s.owner:
owner = owner.up
if owner != nil: add(owner.locals, newp)
else: add(p.g.code, newp)
proc genSym(p: PProc, n: PNode, r: var TCompRes) = proc genSym(p: PProc, n: PNode, r: var TCompRes) =
var s = n.sym var s = n.sym
case s.kind case s.kind
@ -1021,13 +1030,8 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
discard discard
elif sfForward in s.flags: elif sfForward in s.flags:
p.g.forwarded.add(s) p.g.forwarded.add(s)
elif not p.g.generatedSyms.containsOrIncl(s.id): else:
let newp = genProc(p, s) genProcForSymIfNeeded(p, s)
var owner = p
while owner != nil and owner.prc != s.owner:
owner = owner.up
if owner != nil: add(owner.locals, newp)
else: add(p.g.code, newp)
else: else:
if s.loc.r == nil: if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s) internalError(n.info, "symbol has no generated name: " & s.name.s)
@ -1394,6 +1398,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
of mDotDot:
genProcForSymIfNeeded(p, n.sons[0].sym)
genCall(p, n, r)
else: else:
genCall(p, n, r) genCall(p, n, r)
#else internalError(e.info, 'genMagic: ' + magicToStr[op]); #else internalError(e.info, 'genMagic: ' + magicToStr[op]);

View file

@ -17,7 +17,7 @@
import import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg wordrecg, etcpriv
const const
MaxLineLength* = 80 # lines longer than this lead to a warning MaxLineLength* = 80 # lines longer than this lead to a warning
@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool =
proc isNimIdentifier*(s: string): bool = proc isNimIdentifier*(s: string): bool =
if s[0] in SymStartChars: if s[0] in SymStartChars:
var i = 1 var i = 1
while i < s.len: var sLen = s.len
while i < sLen:
if s[i] == '_': if s[i] == '_':
inc(i) inc(i)
if s[i] notin SymChars: return elif isMagicIdentSeparatorRune(cstring s, i):
inc(i, magicIdentSeparatorRuneByteWidth)
if s[i] notin SymChars: return if s[i] notin SymChars: return
inc(i) inc(i)
result = true result = true
@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg) L.dispMessage(info, msg, arg)
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
break
add(tok.literal, '_')
inc(pos)
L.bufpos = pos
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
@ -268,136 +253,195 @@ proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
result = i - start result = i - start
{.pop.} # overflowChecks {.pop.} # overflowChecks
template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
add(t.literal, replacementChar)
inc(L.bufpos)
template eatChar(L: var TLexer, t: var TToken) =
add(t.literal, L.buf[L.bufpos])
inc(L.bufpos)
proc getNumber(L: var TLexer): TToken = proc getNumber(L: var TLexer): TToken =
var var
pos, endpos: int startpos, endpos: int
xi: BiggestInt xi: BiggestInt
# get the base: const literalishChars = { 'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'c',
'C', 'b', 'B', '_', '.', '\''}
const literalishCharsNoDot = literalishChars - {'.'}
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
break
add(tok.literal, '_')
inc(pos)
L.bufpos = pos
proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
L.bufpos = pos
proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
# Used to get slightly human friendlier err messages.
# Note: the erroneous 'O' char in the character set is intentional
const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'}
var msgPos = L.bufpos
var t: TToken
t.literal = ""
L.bufpos = startpos # Use L.bufpos as pos because of matchChars
matchChars(L, t, literalishChars)
# We must verify +/- specifically so that we're not past the literal
if L.buf[L.bufpos] in {'+', '-'} and
L.buf[L.bufpos - 1] in {'e', 'E'}:
add(t.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchChars(L, t, literalishChars)
if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
inc(L.bufpos)
add(t.literal, L.buf[L.bufpos])
matchChars(L, t, {'0'..'9'})
L.bufpos = msgPos
lexMessage(L, msg, t.literal)
result.tokType = tkIntLit # int literal until we know better result.tokType = tkIntLit # int literal until we know better
result.literal = "" result.literal = ""
result.base = base10 # BUGFIX result.base = base10
pos = L.bufpos # make sure the literal is correct for error messages: startpos = L.bufpos
var eallowed = false var isAFloatLiteral = false
if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}: # First stage: find out base, make verifications, build token literal string
matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'}) if L.buf[L.bufpos] == '0' and
L.buf[L.bufpos + 1] in {'X', 'x', 'o', 'O', 'c', 'C', 'b', 'B'}:
eatChar(L, result, '0')
case L.buf[L.bufpos]
of 'O':
lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
of 'x', 'X':
eatChar(L, result, 'x')
matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
of 'o', 'c', 'C':
eatChar(L, result, 'c')
matchUnderscoreChars(L, result, {'0'..'7'})
of 'b', 'B':
eatChar(L, result, 'b')
matchUnderscoreChars(L, result, {'0'..'1'})
else:
internalError(getLineInfo(L), "getNumber")
else: else:
matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'})
eallowed = true
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
add(result.literal, '.')
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'})
eallowed = true
if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
add(result.literal, 'e')
inc(L.bufpos)
if L.buf[L.bufpos] in {'+', '-'}:
add(result.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'}) matchUnderscoreChars(L, result, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
isAFloatLiteral = true
eatChar(L, result, '.')
matchUnderscoreChars(L, result, {'0'..'9'})
if L.buf[L.bufpos] in {'e', 'E'}:
isAFloatLiteral = true
eatChar(L, result, 'e')
if L.buf[L.bufpos] in {'+', '-'}:
eatChar(L, result)
matchUnderscoreChars(L, result, {'0'..'9'})
endpos = L.bufpos endpos = L.bufpos
if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}: # Second stage, find out if there's a datatype postfix and handle it
if L.buf[endpos] == '\'': inc(endpos) var postPos = endpos
L.bufpos = pos # restore position if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
case L.buf[endpos] if L.buf[postPos] == '\'':
inc(postPos)
case L.buf[postPos]
of 'f', 'F': of 'f', 'F':
inc(endpos) inc(postPos)
if (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
result.tokType = tkFloat32Lit result.tokType = tkFloat32Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
result.tokType = tkFloat64Lit result.tokType = tkFloat64Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '1') and elif (L.buf[postPos] == '1') and
(L.buf[endpos + 1] == '2') and (L.buf[postPos + 1] == '2') and
(L.buf[endpos + 2] == '8'): (L.buf[postPos + 2] == '8'):
result.tokType = tkFloat128Lit result.tokType = tkFloat128Lit
inc(endpos, 3) inc(postPos, 3)
else: else: # "f" alone defaults to float32
lexMessage(L, errInvalidNumber, result.literal & "'f" & L.buf[endpos]) result.tokType = tkFloat32Lit
of 'd', 'D': # ad hoc convenience shortcut for f64
inc(postPos)
result.tokType = tkFloat64Lit
of 'i', 'I': of 'i', 'I':
inc(endpos) inc(postPos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
result.tokType = tkInt64Lit result.tokType = tkInt64Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
result.tokType = tkInt32Lit result.tokType = tkInt32Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'): elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
result.tokType = tkInt16Lit result.tokType = tkInt16Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '8'): elif (L.buf[postPos] == '8'):
result.tokType = tkInt8Lit result.tokType = tkInt8Lit
inc(endpos) inc(postPos)
else: else:
lexMessage(L, errInvalidNumber, result.literal & "'i" & L.buf[endpos]) lexMessageLitNum(L, errInvalidNumber, startpos)
of 'u', 'U': of 'u', 'U':
inc(endpos) inc(postPos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
result.tokType = tkUInt64Lit result.tokType = tkUInt64Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
result.tokType = tkUInt32Lit result.tokType = tkUInt32Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'): elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
result.tokType = tkUInt16Lit result.tokType = tkUInt16Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '8'): elif (L.buf[postPos] == '8'):
result.tokType = tkUInt8Lit result.tokType = tkUInt8Lit
inc(endpos) inc(postPos)
else: else:
result.tokType = tkUIntLit result.tokType = tkUIntLit
else: lexMessage(L, errInvalidNumber, result.literal & "'" & L.buf[endpos]) else:
else: lexMessageLitNum(L, errInvalidNumber, startpos)
L.bufpos = pos # restore position # Is there still a literalish char awaiting? Then it's an error!
if L.buf[postPos] in literalishCharsNoDot or
(L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
lexMessageLitNum(L, errInvalidNumber, startpos)
# Third stage, extract actual number
L.bufpos = startpos # restore position
var pos: int = startpos
try: try:
if (L.buf[pos] == '0') and if (L.buf[pos] == '0') and
(L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}): (L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
inc(pos, 2) inc(pos, 2)
xi = 0 # it may be a base prefix xi = 0 # it is a base prefix
case L.buf[pos - 1] # now look at the optional type suffix: case L.buf[pos - 1] # now look at the optional type suffix:
of 'b', 'B': of 'b', 'B':
result.base = base2 result.base = base2
while true: while pos < endpos:
case L.buf[pos] if L.buf[pos] != '_':
of '2'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
if L.buf[pos+1] notin {'0'..'1'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos)
of '0', '1':
xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
inc(pos) inc(pos)
else: break
of 'o', 'c', 'C': of 'o', 'c', 'C':
result.base = base8 result.base = base8
while true: while pos < endpos:
case L.buf[pos] if L.buf[pos] != '_':
of '8'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
if L.buf[pos+1] notin {'0'..'7'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos)
of '0'..'7':
xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
inc(pos) inc(pos)
else: break
of 'O':
lexMessage(L, errInvalidNumber, result.literal)
of 'x', 'X': of 'x', 'X':
result.base = base16 result.base = base16
while true: while pos < endpos:
case L.buf[pos] case L.buf[pos]
of '_': of '_':
if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos) inc(pos)
of '0'..'9': of '0'..'9':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
@ -408,8 +452,10 @@ proc getNumber(L: var TLexer): TToken =
of 'A'..'F': of 'A'..'F':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
inc(pos) inc(pos)
else: break else:
else: internalError(getLineInfo(L), "getNumber") break
else:
internalError(getLineInfo(L), "getNumber")
case result.tokType case result.tokType
of tkIntLit, tkInt64Lit: result.iNumber = xi of tkIntLit, tkInt64Lit: result.iNumber = xi
of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi)))) of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
@ -425,7 +471,7 @@ proc getNumber(L: var TLexer): TToken =
# XXX: Test this on big endian machine! # XXX: Test this on big endian machine!
of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[] of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
else: internalError(getLineInfo(L), "getNumber") else: internalError(getLineInfo(L), "getNumber")
elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or elif isAFloatLiteral or (result.tokType == tkFloat32Lit) or
(result.tokType == tkFloat64Lit): (result.tokType == tkFloat64Lit):
result.fNumber = parseFloat(result.literal) result.fNumber = parseFloat(result.literal)
if result.tokType == tkIntLit: result.tokType = tkFloatLit if result.tokType == tkIntLit: result.tokType = tkFloatLit
@ -441,18 +487,18 @@ proc getNumber(L: var TLexer): TToken =
if result.tokType == tkIntLit: if result.tokType == tkIntLit:
result.tokType = tkInt64Lit result.tokType = tkInt64Lit
elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}: elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}:
lexMessage(L, errNumberOutOfRange, result.literal) lexMessageLitNum(L, errNumberOutOfRange, startpos)
elif result.tokType == tkInt8Lit and elif result.tokType == tkInt8Lit and
(result.iNumber < int8.low or result.iNumber > int8.high): (result.iNumber < int8.low or result.iNumber > int8.high):
lexMessage(L, errNumberOutOfRange, result.literal) lexMessageLitNum(L, errNumberOutOfRange, startpos)
elif result.tokType == tkInt16Lit and elif result.tokType == tkInt16Lit and
(result.iNumber < int16.low or result.iNumber > int16.high): (result.iNumber < int16.low or result.iNumber > int16.high):
lexMessage(L, errNumberOutOfRange, result.literal) lexMessageLitNum(L, errNumberOutOfRange, startpos)
except ValueError: except ValueError:
lexMessage(L, errInvalidNumber, result.literal) lexMessageLitNum(L, errInvalidNumber, startpos)
except OverflowError, RangeError: except OverflowError, RangeError:
lexMessage(L, errNumberOutOfRange, result.literal) lexMessageLitNum(L, errNumberOutOfRange, startpos)
L.bufpos = endpos L.bufpos = postPos
proc handleHexChar(L: var TLexer, xi: var int) = proc handleHexChar(L: var TLexer, xi: var int) =
case L.buf[L.bufpos] case L.buf[L.bufpos]
@ -625,23 +671,34 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip ' inc(L.bufpos) # skip '
proc getSymbol(L: var TLexer, tok: var TToken) = proc getSymbol(L: var TLexer, tok: var TToken) =
var h: THash = 0 var h: Hash = 0
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
while true: while true:
var c = buf[pos] var c = buf[pos]
case c case c
of 'a'..'z', '0'..'9', '\x80'..'\xFF': of 'a'..'z', '0'..'9', '\x80'..'\xFF':
h = h !& ord(c) if c == '\226' and
buf[pos+1] == '\128' and
buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode
if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars:
lexMessage(L, errInvalidToken, "–")
break
inc(pos, magicIdentSeparatorRuneByteWidth)
else:
h = h !& ord(c)
inc(pos)
of 'A'..'Z': of 'A'..'Z':
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c) h = h !& ord(c)
inc(pos)
of '_': of '_':
if buf[pos+1] notin SymChars: if buf[pos+1] notin SymChars:
lexMessage(L, errInvalidToken, "_") lexMessage(L, errInvalidToken, "_")
break break
inc(pos)
else: break else: break
inc(pos)
h = !$h h = !$h
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos L.bufpos = pos
@ -652,7 +709,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol)) tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
proc endOperator(L: var TLexer, tok: var TToken, pos: int, proc endOperator(L: var TLexer, tok: var TToken, pos: int,
hash: THash) {.inline.} = hash: Hash) {.inline.} =
var h = !$hash var h = !$hash
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
@ -662,7 +719,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
proc getOperator(L: var TLexer, tok: var TToken) = proc getOperator(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
var h: THash = 0 var h: Hash = 0
while true: while true:
var c = buf[pos] var c = buf[pos]
if c notin OpChars: break if c notin OpChars: break

View file

@ -17,10 +17,9 @@ type
errIntLiteralExpected, errInvalidCharacterConstant, errIntLiteralExpected, errInvalidCharacterConstant,
errClosingTripleQuoteExpected, errClosingQuoteExpected, errClosingTripleQuoteExpected, errClosingQuoteExpected,
errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
errNewlineExpected, errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
errInvalidModuleName,
errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
@ -143,6 +142,7 @@ const
errInvalidToken: "invalid token: $1", errInvalidToken: "invalid token: $1",
errLineTooLong: "line too long", errLineTooLong: "line too long",
errInvalidNumber: "$1 is not a valid number", errInvalidNumber: "$1 is not a valid number",
errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
errNumberOutOfRange: "number $1 out of valid range", errNumberOutOfRange: "number $1 out of valid range",
errNnotAllowedInCharacter: "\\n not allowed in character literal", errNnotAllowedInCharacter: "\\n not allowed in character literal",
errClosingBracketExpected: "closing ']' expected, but end of file reached", errClosingBracketExpected: "closing ']' expected, but end of file reached",

View file

@ -12,7 +12,7 @@
import import
hashes, ast, astalgo, types hashes, ast, astalgo, types
proc hashTree(n: PNode): THash = proc hashTree(n: PNode): Hash =
if n == nil: return if n == nil: return
result = ord(n.kind) result = ord(n.kind)
case n.kind case n.kind
@ -53,8 +53,8 @@ proc treesEquivalent(a, b: PNode): bool =
result = true result = true
if result: result = sameTypeOrNil(a.typ, b.typ) if result: result = sameTypeOrNil(a.typ, b.typ)
proc nodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int = proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
var h: THash = k and high(t.data) var h: Hash = k and high(t.data)
while t.data[h].key != nil: while t.data[h].key != nil:
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key): if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
return h return h
@ -66,9 +66,9 @@ proc nodeTableGet*(t: TNodeTable, key: PNode): int =
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: result = low(int) else: result = low(int)
proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode, proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
val: int) = val: int) =
var h: THash = k and high(data) var h: Hash = k and high(data)
while data[h].key != nil: h = nextTry(h, high(data)) while data[h].key != nil: h = nextTry(h, high(data))
assert(data[h].key == nil) assert(data[h].key == nil)
data[h].h = k data[h].h = k
@ -77,7 +77,7 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) = proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
var n: TNodePairSeq var n: TNodePairSeq
var k: THash = hashTree(key) var k: Hash = hashTree(key)
var index = nodeTableRawGet(t, k, key) var index = nodeTableRawGet(t, k, key)
if index >= 0: if index >= 0:
assert(t.data[index].key != nil) assert(t.data[index].key != nil)
@ -94,7 +94,7 @@ proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int = proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
var n: TNodePairSeq var n: TNodePairSeq
var k: THash = hashTree(key) var k: Hash = hashTree(key)
var index = nodeTableRawGet(t, k, key) var index = nodeTableRawGet(t, k, key)
if index >= 0: if index >= 0:
assert(t.data[index].key != nil) assert(t.data[index].key != nil)

View file

@ -276,7 +276,7 @@ Numerical constants are of a single type and have the form::
bindigit = '0'..'1' bindigit = '0'..'1'
HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
DEC_LIT = digit ( ['_'] digit )* DEC_LIT = digit ( ['_'] digit )*
OCT_LIT = '0o' octdigit ( ['_'] octdigit )* OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
INT_LIT = HEX_LIT INT_LIT = HEX_LIT
@ -297,15 +297,17 @@ Numerical constants are of a single type and have the form::
exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent) FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent)
FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32' FLOAT32_SUFFIX = ('f' | 'F') ['32']
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32' FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64' | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64' FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
As can be seen in the productions, numerical constants can contain underscores As can be seen in the productions, numerical constants can contain underscores
for readability. Integer and floating point literals may be given in decimal (no for readability. Integer and floating point literals may be given in decimal (no
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and hexadecimal prefix), binary (prefix ``0b``), octal (prefix ``0o`` or ``0c``) and hexadecimal
(prefix ``0x``) notation. (prefix ``0x``) notation.
There exists a literal for each numerical type that is There exists a literal for each numerical type that is
@ -331,8 +333,11 @@ The type suffixes are:
``'u16`` uint16 ``'u16`` uint16
``'u32`` uint32 ``'u32`` uint32
``'u64`` uint64 ``'u64`` uint64
``'f`` float32
``'d`` float64
``'f32`` float32 ``'f32`` float32
``'f64`` float64 ``'f64`` float64
``'f128`` float128
================= ========================= ================= =========================
Floating point literals may also be in binary, octal or hexadecimal Floating point literals may also be in binary, octal or hexadecimal
@ -344,8 +349,8 @@ is approximately 1.72826e35 according to the IEEE floating point standard.
Operators Operators
--------- ---------
In Nim one can define his own operators. An operator is any Nim allows user defined operators. An operator is any combination of the
combination of the following characters:: following characters::
= + - * / < > = + - * / < >
@ $ ~ & % | @ $ ~ & % |
@ -355,7 +360,7 @@ These keywords are also operators:
``and or not xor shl shr div mod in notin is isnot of``. ``and or not xor shl shr div mod in notin is isnot of``.
`=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they
are used for other notational purposes. are used for other notational purposes.
``*:`` is as a special case the two tokens `*`:tok: and `:`:tok: ``*:`` is as a special case the two tokens `*`:tok: and `:`:tok:
(to support ``var v*: T``). (to support ``var v*: T``).

View file

@ -12,44 +12,46 @@
include "system/syslocks" include "system/syslocks"
type type
TLock* = TSysLock ## Nim lock; whether this is re-entrant Lock* = SysLock ## Nim lock; whether this is re-entrant
## or not is unspecified! ## or not is unspecified!
TCond* = TSysCond ## Nim condition variable
proc initLock*(lock: var TLock) {.inline.} = Cond* = SysCond ## Nim condition variable
{.deprecated: [TLock: Lock, TCond: Cond].}
proc initLock*(lock: var Lock) {.inline.} =
## Initializes the given lock. ## Initializes the given lock.
initSysLock(lock) initSysLock(lock)
proc deinitLock*(lock: var TLock) {.inline.} = proc deinitLock*(lock: var Lock) {.inline.} =
## Frees the resources associated with the lock. ## Frees the resources associated with the lock.
deinitSys(lock) deinitSys(lock)
proc tryAcquire*(lock: var TLock): bool = proc tryAcquire*(lock: var Lock): bool =
## Tries to acquire the given lock. Returns `true` on success. ## Tries to acquire the given lock. Returns `true` on success.
result = tryAcquireSys(lock) result = tryAcquireSys(lock)
proc acquire*(lock: var TLock) = proc acquire*(lock: var Lock) =
## Acquires the given lock. ## Acquires the given lock.
acquireSys(lock) acquireSys(lock)
proc release*(lock: var TLock) = proc release*(lock: var Lock) =
## Releases the given lock. ## Releases the given lock.
releaseSys(lock) releaseSys(lock)
proc initCond*(cond: var TCond) {.inline.} = proc initCond*(cond: var Cond) {.inline.} =
## Initializes the given condition variable. ## Initializes the given condition variable.
initSysCond(cond) initSysCond(cond)
proc deinitCond*(cond: var TCond) {.inline.} = proc deinitCond*(cond: var Cond) {.inline.} =
## Frees the resources associated with the lock. ## Frees the resources associated with the lock.
deinitSysCond(cond) deinitSysCond(cond)
proc wait*(cond: var TCond, lock: var TLock) {.inline.} = proc wait*(cond: var Cond, lock: var Lock) {.inline.} =
## waits on the condition variable `cond`. ## waits on the condition variable `cond`.
waitSysCond(cond, lock) waitSysCond(cond, lock)
proc signal*(cond: var TCond) {.inline.} = proc signal*(cond: var Cond) {.inline.} =
## sends a signal to the condition variable `cond`. ## sends a signal to the condition variable `cond`.
signalSysCond(cond) signalSysCond(cond)

View file

@ -9,7 +9,7 @@
## This module implements an interface to Nim's `runtime type information`:idx: ## This module implements an interface to Nim's `runtime type information`:idx:
## (`RTTI`:idx:). ## (`RTTI`:idx:).
## Note that even though ``TAny`` and its operations hide the nasty low level ## Note that even though ``Any`` and its operations hide the nasty low level
## details from its clients, it remains inherently unsafe! ## details from its clients, it remains inherently unsafe!
## ##
## See the `marshal <marshal.html>`_ module for what this module allows you ## See the `marshal <marshal.html>`_ module for what this module allows you
@ -23,7 +23,7 @@ include "system/hti.nim"
{.pop.} {.pop.}
type type
TAnyKind* = enum ## what kind of ``any`` it is AnyKind* = enum ## what kind of ``any`` it is
akNone = 0, ## invalid any akNone = 0, ## invalid any
akBool = 1, ## any represents a ``bool`` akBool = 1, ## any represents a ``bool``
akChar = 2, ## any represents a ``char`` akChar = 2, ## any represents a ``char``
@ -55,9 +55,9 @@ type
akUInt32 = 43, ## any represents an unsigned int32 akUInt32 = 43, ## any represents an unsigned int32
akUInt64 = 44, ## any represents an unsigned int64 akUInt64 = 44, ## any represents an unsigned int64
TAny* = object ## can represent any nim value; NOTE: the wrapped Any* = object ## can represent any nim value; NOTE: the wrapped
## value can be modified with its wrapper! This means ## value can be modified with its wrapper! This means
## that ``TAny`` keeps a non-traced pointer to its ## that ``Any`` keeps a non-traced pointer to its
## wrapped value and **must not** live longer than ## wrapped value and **must not** live longer than
## its wrapped value. ## its wrapped value.
value: pointer value: pointer
@ -69,6 +69,7 @@ type
TGenericSeq {.importc.} = object TGenericSeq {.importc.} = object
len, space: int len, space: int
PGenSeq = ptr TGenericSeq PGenSeq = ptr TGenericSeq
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
const const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))
@ -103,58 +104,58 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
else: else:
result = n.sons[n.len] result = n.sons[n.len]
proc newAny(value: pointer, rawType: PNimType): TAny = proc newAny(value: pointer, rawType: PNimType): Any =
result.value = value result.value = value
result.rawType = rawType result.rawType = rawType
when declared(system.TVarSlot): when declared(system.VarSlot):
proc toAny*(x: TVarSlot): TAny {.inline.} = proc toAny*(x: VarSlot): Any {.inline.} =
## constructs a ``TAny`` object from a variable slot ``x``. ## constructs a ``Any`` object from a variable slot ``x``.
## This captures `x`'s address, so `x` can be modified with its ## This captures `x`'s address, so `x` can be modified with its
## ``TAny`` wrapper! The client needs to ensure that the wrapper ## ``Any`` wrapper! The client needs to ensure that the wrapper
## **does not** live longer than `x`! ## **does not** live longer than `x`!
## This is provided for easier reflection capabilities of a debugger. ## This is provided for easier reflection capabilities of a debugger.
result.value = x.address result.value = x.address
result.rawType = x.typ result.rawType = x.typ
proc toAny*[T](x: var T): TAny {.inline.} = proc toAny*[T](x: var T): Any {.inline.} =
## constructs a ``TAny`` object from `x`. This captures `x`'s address, so ## constructs a ``Any`` object from `x`. This captures `x`'s address, so
## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure ## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
## that the wrapper **does not** live longer than `x`! ## that the wrapper **does not** live longer than `x`!
result.value = addr(x) result.value = addr(x)
result.rawType = cast[PNimType](getTypeInfo(x)) result.rawType = cast[PNimType](getTypeInfo(x))
proc kind*(x: TAny): TAnyKind {.inline.} = proc kind*(x: Any): AnyKind {.inline.} =
## get the type kind ## get the type kind
result = TAnyKind(ord(x.rawType.kind)) result = AnyKind(ord(x.rawType.kind))
proc size*(x: TAny): int {.inline.} = proc size*(x: Any): int {.inline.} =
## returns the size of `x`'s type. ## returns the size of `x`'s type.
result = x.rawType.size result = x.rawType.size
proc baseTypeKind*(x: TAny): TAnyKind {.inline.} = proc baseTypeKind*(x: Any): AnyKind {.inline.} =
## get the base type's kind; ``akNone`` is returned if `x` has no base type. ## get the base type's kind; ``akNone`` is returned if `x` has no base type.
if x.rawType.base != nil: if x.rawType.base != nil:
result = TAnyKind(ord(x.rawType.base.kind)) result = AnyKind(ord(x.rawType.base.kind))
proc baseTypeSize*(x: TAny): int {.inline.} = proc baseTypeSize*(x: Any): int {.inline.} =
## returns the size of `x`'s basetype. ## returns the size of `x`'s basetype.
if x.rawType.base != nil: if x.rawType.base != nil:
result = x.rawType.base.size result = x.rawType.base.size
proc invokeNew*(x: TAny) = proc invokeNew*(x: Any) =
## performs ``new(x)``. `x` needs to represent a ``ref``. ## performs ``new(x)``. `x` needs to represent a ``ref``.
assert x.rawType.kind == tyRef assert x.rawType.kind == tyRef
var z = newObj(x.rawType, x.rawType.base.size) var z = newObj(x.rawType, x.rawType.base.size)
genericAssign(x.value, addr(z), x.rawType) genericAssign(x.value, addr(z), x.rawType)
proc invokeNewSeq*(x: TAny, len: int) = proc invokeNewSeq*(x: Any, len: int) =
## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``. ## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``.
assert x.rawType.kind == tySequence assert x.rawType.kind == tySequence
var z = newSeq(x.rawType, len) var z = newSeq(x.rawType, len)
genericShallowAssign(x.value, addr(z), x.rawType) genericShallowAssign(x.value, addr(z), x.rawType)
proc extendSeq*(x: TAny) = proc extendSeq*(x: Any) =
## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``. ## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
assert x.rawType.kind == tySequence assert x.rawType.kind == tySequence
var y = cast[ptr PGenSeq](x.value)[] var y = cast[ptr PGenSeq](x.value)[]
@ -164,7 +165,7 @@ proc extendSeq*(x: TAny) =
cast[ppointer](x.value)[] = z cast[ppointer](x.value)[] = z
#genericShallowAssign(x.value, addr(z), x.rawType) #genericShallowAssign(x.value, addr(z), x.rawType)
proc setObjectRuntimeType*(x: TAny) = proc setObjectRuntimeType*(x: Any) =
## this needs to be called to set `x`'s runtime object type field. ## this needs to be called to set `x`'s runtime object type field.
assert x.rawType.kind == tyObject assert x.rawType.kind == tyObject
objectInit(x.value, x.rawType) objectInit(x.value, x.rawType)
@ -173,7 +174,7 @@ proc skipRange(x: PNimType): PNimType {.inline.} =
result = x result = x
if result.kind == tyRange: result = result.base if result.kind == tyRange: result = result.base
proc `[]`*(x: TAny, i: int): TAny = proc `[]`*(x: Any, i: int): Any =
## accessor for an any `x` that represents an array or a sequence. ## accessor for an any `x` that represents an array or a sequence.
case x.rawType.kind case x.rawType.kind
of tyArray: of tyArray:
@ -190,7 +191,7 @@ proc `[]`*(x: TAny, i: int): TAny =
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base) return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
else: assert false else: assert false
proc `[]=`*(x: TAny, i: int, y: TAny) = proc `[]=`*(x: Any, i: int, y: Any) =
## accessor for an any `x` that represents an array or a sequence. ## accessor for an any `x` that represents an array or a sequence.
case x.rawType.kind case x.rawType.kind
of tyArray: of tyArray:
@ -209,7 +210,7 @@ proc `[]=`*(x: TAny, i: int, y: TAny) =
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType) genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
else: assert false else: assert false
proc len*(x: TAny): int = proc len*(x: Any): int =
## len for an any `x` that represents an array or a sequence. ## len for an any `x` that represents an array or a sequence.
case x.rawType.kind case x.rawType.kind
of tyArray: result = x.rawType.size div x.rawType.base.size of tyArray: result = x.rawType.size div x.rawType.base.size
@ -217,20 +218,20 @@ proc len*(x: TAny): int =
else: assert false else: assert false
proc base*(x: TAny): TAny = proc base*(x: Any): Any =
## returns base TAny (useful for inherited object types). ## returns base Any (useful for inherited object types).
result.rawType = x.rawType.base result.rawType = x.rawType.base
result.value = x.value result.value = x.value
proc isNil*(x: TAny): bool = proc isNil*(x: Any): bool =
## `isNil` for an any `x` that represents a sequence, string, cstring, ## `isNil` for an any `x` that represents a sequence, string, cstring,
## proc or some pointer type. ## proc or some pointer type.
assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer, assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
tySequence, tyProc} tySequence, tyProc}
result = isNil(cast[ppointer](x.value)[]) result = isNil(cast[ppointer](x.value)[])
proc getPointer*(x: TAny): pointer = proc getPointer*(x: Any): pointer =
## retrieve the pointer value out of `x`. ``x`` needs to be of kind ## retrieve the pointer value out of `x`. ``x`` needs to be of kind
## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
## ``akPointer``, ``akSequence``. ## ``akPointer``, ``akSequence``.
@ -238,7 +239,7 @@ proc getPointer*(x: TAny): pointer =
tySequence, tyProc} tySequence, tyProc}
result = cast[ppointer](x.value)[] result = cast[ppointer](x.value)[]
proc setPointer*(x: TAny, y: pointer) = proc setPointer*(x: Any, y: pointer) =
## sets the pointer value of `x`. ``x`` needs to be of kind ## sets the pointer value of `x`. ``x`` needs to be of kind
## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
## ``akPointer``, ``akSequence``. ## ``akPointer``, ``akSequence``.
@ -247,7 +248,7 @@ proc setPointer*(x: TAny, y: pointer) =
cast[ppointer](x.value)[] = y cast[ppointer](x.value)[] = y
proc fieldsAux(p: pointer, n: ptr TNimNode, proc fieldsAux(p: pointer, n: ptr TNimNode,
ret: var seq[tuple[name: cstring, any: TAny]]) = ret: var seq[tuple[name: cstring, any: Any]]) =
case n.kind case n.kind
of nkNone: assert(false) of nkNone: assert(false)
of nkSlot: of nkSlot:
@ -260,7 +261,7 @@ proc fieldsAux(p: pointer, n: ptr TNimNode,
ret.add((n.name, newAny(p +!! n.offset, n.typ))) ret.add((n.name, newAny(p +!! n.offset, n.typ)))
if m != nil: fieldsAux(p, m, ret) if m != nil: fieldsAux(p, m, ret)
iterator fields*(x: TAny): tuple[name: string, any: TAny] = iterator fields*(x: Any): tuple[name: string, any: Any] =
## iterates over every active field of the any `x` that represents an object ## iterates over every active field of the any `x` that represents an object
## or a tuple. ## or a tuple.
assert x.rawType.kind in {tyTuple, tyObject} assert x.rawType.kind in {tyTuple, tyObject}
@ -269,7 +270,7 @@ iterator fields*(x: TAny): tuple[name: string, any: TAny] =
# XXX BUG: does not work yet, however is questionable anyway # XXX BUG: does not work yet, however is questionable anyway
when false: when false:
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
var ret: seq[tuple[name: cstring, any: TAny]] = @[] var ret: seq[tuple[name: cstring, any: Any]] = @[]
if t.kind == tyObject: if t.kind == tyObject:
while true: while true:
fieldsAux(p, t.node, ret) fieldsAux(p, t.node, ret)
@ -314,7 +315,7 @@ proc getFieldNode(p: pointer, n: ptr TNimNode,
var m = selectBranch(p, n) var m = selectBranch(p, n)
if m != nil: result = getFieldNode(p, m, name) if m != nil: result = getFieldNode(p, m, name)
proc `[]=`*(x: TAny, fieldName: string, value: TAny) = proc `[]=`*(x: Any, fieldName: string, value: Any) =
## sets a field of `x`; `x` represents an object or a tuple. ## sets a field of `x`; `x` represents an object or a tuple.
var t = x.rawType var t = x.rawType
# XXX BUG: does not work yet, however is questionable anyway # XXX BUG: does not work yet, however is questionable anyway
@ -328,7 +329,7 @@ proc `[]=`*(x: TAny, fieldName: string, value: TAny) =
else: else:
raise newException(ValueError, "invalid field name: " & fieldName) raise newException(ValueError, "invalid field name: " & fieldName)
proc `[]`*(x: TAny, fieldName: string): TAny = proc `[]`*(x: Any, fieldName: string): Any =
## gets a field of `x`; `x` represents an object or a tuple. ## gets a field of `x`; `x` represents an object or a tuple.
var t = x.rawType var t = x.rawType
# XXX BUG: does not work yet, however is questionable anyway # XXX BUG: does not work yet, however is questionable anyway
@ -342,44 +343,44 @@ proc `[]`*(x: TAny, fieldName: string): TAny =
else: else:
raise newException(ValueError, "invalid field name: " & fieldName) raise newException(ValueError, "invalid field name: " & fieldName)
proc `[]`*(x: TAny): TAny = proc `[]`*(x: Any): Any =
## dereference operation for the any `x` that represents a ptr or a ref. ## dereference operation for the any `x` that represents a ptr or a ref.
assert x.rawType.kind in {tyRef, tyPtr} assert x.rawType.kind in {tyRef, tyPtr}
result.value = cast[ppointer](x.value)[] result.value = cast[ppointer](x.value)[]
result.rawType = x.rawType.base result.rawType = x.rawType.base
proc `[]=`*(x, y: TAny) = proc `[]=`*(x, y: Any) =
## dereference operation for the any `x` that represents a ptr or a ref. ## dereference operation for the any `x` that represents a ptr or a ref.
assert x.rawType.kind in {tyRef, tyPtr} assert x.rawType.kind in {tyRef, tyPtr}
assert y.rawType == x.rawType.base assert y.rawType == x.rawType.base
genericAssign(cast[ppointer](x.value)[], y.value, y.rawType) genericAssign(cast[ppointer](x.value)[], y.value, y.rawType)
proc getInt*(x: TAny): int = proc getInt*(x: Any): int =
## retrieve the int value out of `x`. `x` needs to represent an int. ## retrieve the int value out of `x`. `x` needs to represent an int.
assert skipRange(x.rawType).kind == tyInt assert skipRange(x.rawType).kind == tyInt
result = cast[ptr int](x.value)[] result = cast[ptr int](x.value)[]
proc getInt8*(x: TAny): int8 = proc getInt8*(x: Any): int8 =
## retrieve the int8 value out of `x`. `x` needs to represent an int8. ## retrieve the int8 value out of `x`. `x` needs to represent an int8.
assert skipRange(x.rawType).kind == tyInt8 assert skipRange(x.rawType).kind == tyInt8
result = cast[ptr int8](x.value)[] result = cast[ptr int8](x.value)[]
proc getInt16*(x: TAny): int16 = proc getInt16*(x: Any): int16 =
## retrieve the int16 value out of `x`. `x` needs to represent an int16. ## retrieve the int16 value out of `x`. `x` needs to represent an int16.
assert skipRange(x.rawType).kind == tyInt16 assert skipRange(x.rawType).kind == tyInt16
result = cast[ptr int16](x.value)[] result = cast[ptr int16](x.value)[]
proc getInt32*(x: TAny): int32 = proc getInt32*(x: Any): int32 =
## retrieve the int32 value out of `x`. `x` needs to represent an int32. ## retrieve the int32 value out of `x`. `x` needs to represent an int32.
assert skipRange(x.rawType).kind == tyInt32 assert skipRange(x.rawType).kind == tyInt32
result = cast[ptr int32](x.value)[] result = cast[ptr int32](x.value)[]
proc getInt64*(x: TAny): int64 = proc getInt64*(x: Any): int64 =
## retrieve the int64 value out of `x`. `x` needs to represent an int64. ## retrieve the int64 value out of `x`. `x` needs to represent an int64.
assert skipRange(x.rawType).kind == tyInt64 assert skipRange(x.rawType).kind == tyInt64
result = cast[ptr int64](x.value)[] result = cast[ptr int64](x.value)[]
proc getBiggestInt*(x: TAny): BiggestInt = proc getBiggestInt*(x: Any): BiggestInt =
## retrieve the integer value out of `x`. `x` needs to represent ## retrieve the integer value out of `x`. `x` needs to represent
## some integer, a bool, a char, an enum or a small enough bit set. ## some integer, a bool, a char, an enum or a small enough bit set.
## The value might be sign-extended to ``BiggestInt``. ## The value might be sign-extended to ``BiggestInt``.
@ -405,7 +406,7 @@ proc getBiggestInt*(x: TAny): BiggestInt =
of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[]) of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[])
else: assert false else: assert false
proc setBiggestInt*(x: TAny, y: BiggestInt) = proc setBiggestInt*(x: Any, y: BiggestInt) =
## sets the integer value of `x`. `x` needs to represent ## sets the integer value of `x`. `x` needs to represent
## some integer, a bool, a char, an enum or a small enough bit set. ## some integer, a bool, a char, an enum or a small enough bit set.
var t = skipRange(x.rawType) var t = skipRange(x.rawType)
@ -430,36 +431,36 @@ proc setBiggestInt*(x: TAny, y: BiggestInt) =
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y) of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
else: assert false else: assert false
proc getUInt*(x: TAny): uint = proc getUInt*(x: Any): uint =
## retrieve the uint value out of `x`, `x` needs to represent an uint. ## retrieve the uint value out of `x`, `x` needs to represent an uint.
assert skipRange(x.rawType).kind == tyUInt assert skipRange(x.rawType).kind == tyUInt
result = cast[ptr uint](x.value)[] result = cast[ptr uint](x.value)[]
proc getUInt8*(x: TAny): uint8 = proc getUInt8*(x: Any): uint8 =
## retrieve the uint8 value out of `x`, `x` needs to represent an ## retrieve the uint8 value out of `x`, `x` needs to represent an
## uint8. ## uint8.
assert skipRange(x.rawType).kind == tyUInt8 assert skipRange(x.rawType).kind == tyUInt8
result = cast[ptr uint8](x.value)[] result = cast[ptr uint8](x.value)[]
proc getUInt16*(x: TAny): uint16 = proc getUInt16*(x: Any): uint16 =
## retrieve the uint16 value out of `x`, `x` needs to represent an ## retrieve the uint16 value out of `x`, `x` needs to represent an
## uint16. ## uint16.
assert skipRange(x.rawType).kind == tyUInt16 assert skipRange(x.rawType).kind == tyUInt16
result = cast[ptr uint16](x.value)[] result = cast[ptr uint16](x.value)[]
proc getUInt32*(x: TAny): uint32 = proc getUInt32*(x: Any): uint32 =
## retrieve the uint32 value out of `x`, `x` needs to represent an ## retrieve the uint32 value out of `x`, `x` needs to represent an
## uint32. ## uint32.
assert skipRange(x.rawType).kind == tyUInt32 assert skipRange(x.rawType).kind == tyUInt32
result = cast[ptr uint32](x.value)[] result = cast[ptr uint32](x.value)[]
proc getUInt64*(x: TAny): uint64 = proc getUInt64*(x: Any): uint64 =
## retrieve the uint64 value out of `x`, `x` needs to represent an ## retrieve the uint64 value out of `x`, `x` needs to represent an
## uint64. ## uint64.
assert skipRange(x.rawType).kind == tyUInt64 assert skipRange(x.rawType).kind == tyUInt64
result = cast[ptr uint64](x.value)[] result = cast[ptr uint64](x.value)[]
proc getBiggestUint*(x: TAny): uint64 = proc getBiggestUint*(x: Any): uint64 =
## retrieve the unsigned integer value out of `x`. `x` needs to ## retrieve the unsigned integer value out of `x`. `x` needs to
## represent an unsigned integer. ## represent an unsigned integer.
var t = skipRange(x.rawType) var t = skipRange(x.rawType)
@ -471,7 +472,7 @@ proc getBiggestUint*(x: TAny): uint64 =
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[]) of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
else: assert false else: assert false
proc setBiggestUint*(x: TAny; y: uint64) = proc setBiggestUint*(x: Any; y: uint64) =
## sets the unsigned integer value of `c`. `c` needs to represent an ## sets the unsigned integer value of `c`. `c` needs to represent an
## unsigned integer. ## unsigned integer.
var t = skipRange(x.rawType) var t = skipRange(x.rawType)
@ -483,25 +484,25 @@ proc setBiggestUint*(x: TAny; y: uint64) =
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y) of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
else: assert false else: assert false
proc getChar*(x: TAny): char = proc getChar*(x: Any): char =
## retrieve the char value out of `x`. `x` needs to represent a char. ## retrieve the char value out of `x`. `x` needs to represent a char.
var t = skipRange(x.rawType) var t = skipRange(x.rawType)
assert t.kind == tyChar assert t.kind == tyChar
result = cast[ptr char](x.value)[] result = cast[ptr char](x.value)[]
proc getBool*(x: TAny): bool = proc getBool*(x: Any): bool =
## retrieve the bool value out of `x`. `x` needs to represent a bool. ## retrieve the bool value out of `x`. `x` needs to represent a bool.
var t = skipRange(x.rawType) var t = skipRange(x.rawType)
assert t.kind == tyBool assert t.kind == tyBool
result = cast[ptr bool](x.value)[] result = cast[ptr bool](x.value)[]
proc skipRange*(x: TAny): TAny = proc skipRange*(x: Any): Any =
## skips the range information of `x`. ## skips the range information of `x`.
assert x.rawType.kind == tyRange assert x.rawType.kind == tyRange
result.rawType = x.rawType.base result.rawType = x.rawType.base
result.value = x.value result.value = x.value
proc getEnumOrdinal*(x: TAny, name: string): int = proc getEnumOrdinal*(x: Any, name: string): int =
## gets the enum field ordinal from `name`. `x` needs to represent an enum ## gets the enum field ordinal from `name`. `x` needs to represent an enum
## but is only used to access the type information. In case of an error ## but is only used to access the type information. In case of an error
## ``low(int)`` is returned. ## ``low(int)`` is returned.
@ -517,7 +518,7 @@ proc getEnumOrdinal*(x: TAny, name: string): int =
return s[i].offset return s[i].offset
result = low(int) result = low(int)
proc getEnumField*(x: TAny, ordinalValue: int): string = proc getEnumField*(x: Any, ordinalValue: int): string =
## gets the enum field name as a string. `x` needs to represent an enum ## gets the enum field name as a string. `x` needs to represent an enum
## but is only used to access the type information. The field name of ## but is only used to access the type information. The field name of
## `ordinalValue` is returned. ## `ordinalValue` is returned.
@ -535,26 +536,26 @@ proc getEnumField*(x: TAny, ordinalValue: int): string =
if s[i].offset == e: return $s[i].name if s[i].offset == e: return $s[i].name
result = $e result = $e
proc getEnumField*(x: TAny): string = proc getEnumField*(x: Any): string =
## gets the enum field name as a string. `x` needs to represent an enum. ## gets the enum field name as a string. `x` needs to represent an enum.
result = getEnumField(x, getBiggestInt(x).int) result = getEnumField(x, getBiggestInt(x).int)
proc getFloat*(x: TAny): float = proc getFloat*(x: Any): float =
## retrieve the float value out of `x`. `x` needs to represent an float. ## retrieve the float value out of `x`. `x` needs to represent an float.
assert skipRange(x.rawType).kind == tyFloat assert skipRange(x.rawType).kind == tyFloat
result = cast[ptr float](x.value)[] result = cast[ptr float](x.value)[]
proc getFloat32*(x: TAny): float32 = proc getFloat32*(x: Any): float32 =
## retrieve the float32 value out of `x`. `x` needs to represent an float32. ## retrieve the float32 value out of `x`. `x` needs to represent an float32.
assert skipRange(x.rawType).kind == tyFloat32 assert skipRange(x.rawType).kind == tyFloat32
result = cast[ptr float32](x.value)[] result = cast[ptr float32](x.value)[]
proc getFloat64*(x: TAny): float64 = proc getFloat64*(x: Any): float64 =
## retrieve the float64 value out of `x`. `x` needs to represent an float64. ## retrieve the float64 value out of `x`. `x` needs to represent an float64.
assert skipRange(x.rawType).kind == tyFloat64 assert skipRange(x.rawType).kind == tyFloat64
result = cast[ptr float64](x.value)[] result = cast[ptr float64](x.value)[]
proc getBiggestFloat*(x: TAny): BiggestFloat = proc getBiggestFloat*(x: Any): BiggestFloat =
## retrieve the float value out of `x`. `x` needs to represent ## retrieve the float value out of `x`. `x` needs to represent
## some float. The value is extended to ``BiggestFloat``. ## some float. The value is extended to ``BiggestFloat``.
case skipRange(x.rawType).kind case skipRange(x.rawType).kind
@ -563,7 +564,7 @@ proc getBiggestFloat*(x: TAny): BiggestFloat =
of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[]) of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[])
else: assert false else: assert false
proc setBiggestFloat*(x: TAny, y: BiggestFloat) = proc setBiggestFloat*(x: Any, y: BiggestFloat) =
## sets the float value of `x`. `x` needs to represent ## sets the float value of `x`. `x` needs to represent
## some float. ## some float.
case skipRange(x.rawType).kind case skipRange(x.rawType).kind
@ -572,29 +573,29 @@ proc setBiggestFloat*(x: TAny, y: BiggestFloat) =
of tyFloat64: cast[ptr float64](x.value)[] = y of tyFloat64: cast[ptr float64](x.value)[] = y
else: assert false else: assert false
proc getString*(x: TAny): string = proc getString*(x: Any): string =
## retrieve the string value out of `x`. `x` needs to represent a string. ## retrieve the string value out of `x`. `x` needs to represent a string.
assert x.rawType.kind == tyString assert x.rawType.kind == tyString
if not isNil(cast[ptr pointer](x.value)[]): if not isNil(cast[ptr pointer](x.value)[]):
result = cast[ptr string](x.value)[] result = cast[ptr string](x.value)[]
proc setString*(x: TAny, y: string) = proc setString*(x: Any, y: string) =
## sets the string value of `x`. `x` needs to represent a string. ## sets the string value of `x`. `x` needs to represent a string.
assert x.rawType.kind == tyString assert x.rawType.kind == tyString
cast[ptr string](x.value)[] = y cast[ptr string](x.value)[] = y
proc getCString*(x: TAny): cstring = proc getCString*(x: Any): cstring =
## retrieve the cstring value out of `x`. `x` needs to represent a cstring. ## retrieve the cstring value out of `x`. `x` needs to represent a cstring.
assert x.rawType.kind == tyCString assert x.rawType.kind == tyCString
result = cast[ptr cstring](x.value)[] result = cast[ptr cstring](x.value)[]
proc assign*(x, y: TAny) = proc assign*(x, y: Any) =
## copies the value of `y` to `x`. The assignment operator for ``TAny`` ## copies the value of `y` to `x`. The assignment operator for ``Any``
## does NOT do this; it performs a shallow copy instead! ## does NOT do this; it performs a shallow copy instead!
assert y.rawType == x.rawType assert y.rawType == x.rawType
genericAssign(x.value, y.value, y.rawType) genericAssign(x.value, y.value, y.rawType)
iterator elements*(x: TAny): int = iterator elements*(x: Any): int =
## iterates over every element of `x` that represents a Nim bitset. ## iterates over every element of `x` that represents a Nim bitset.
assert x.rawType.kind == tySet assert x.rawType.kind == tySet
var typ = x.rawType var typ = x.rawType
@ -616,7 +617,7 @@ iterator elements*(x: TAny): int =
if (u and (1'i64 shl int64(i))) != 0'i64: if (u and (1'i64 shl int64(i))) != 0'i64:
yield i+typ.node.len yield i+typ.node.len
proc inclSetElement*(x: TAny, elem: int) = proc inclSetElement*(x: Any, elem: int) =
## includes an element `elem` in `x`. `x` needs to represent a Nim bitset. ## includes an element `elem` in `x`. `x` needs to represent a Nim bitset.
assert x.rawType.kind == tySet assert x.rawType.kind == tySet
var typ = x.rawType var typ = x.rawType

View file

@ -13,27 +13,28 @@
import strutils, mysql import strutils, mysql
type type
TDbConn* = PMySQL ## encapsulates a database connection DbConn* = PMySQL ## encapsulates a database connection
TRow* = seq[string] ## a row of a dataset. NULL database values will be Row* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string. ## transformed always to the empty string.
EDb* = object of IOError ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
TSqlQuery* = distinct string ## an SQL query string SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a TSqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = TSqlQuery(query) result = SqlQuery(query)
proc dbError(db: TDbConn) {.noreturn.} = proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
new(e) new(e)
@ -48,7 +49,7 @@ proc dbError*(msg: string) {.noreturn.} =
raise e raise e
when false: when false:
proc dbQueryOpt*(db: TDbConn, query: string, args: varargs[string, `$`]) = proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
var stmt = mysql_stmt_init(db) var stmt = mysql_stmt_init(db)
if stmt == nil: dbError(db) if stmt == nil: dbError(db)
if mysql_stmt_prepare(stmt, query, len(query)) != 0: if mysql_stmt_prepare(stmt, query, len(query)) != 0:
@ -65,7 +66,7 @@ proc dbQuote*(s: string): string =
else: add(result, c) else: add(result, c)
add(result, '\'') add(result, '\'')
proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string = proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
result = "" result = ""
var a = 0 var a = 0
for c in items(string(formatstr)): for c in items(string(formatstr)):
@ -78,23 +79,23 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
else: else:
add(result, c) add(result, c)
proc tryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool {. proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [FReadDB, FWriteDb].} = tags: [FReadDB, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args) var q = dbFormat(query, args)
return mysql.realQuery(db, q, q.len) == 0'i32 return mysql.realQuery(db, q, q.len) == 0'i32
proc rawExec(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) = proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
var q = dbFormat(query, args) var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db) if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {. proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDB, FWriteDb].} = tags: [FReadDB, FWriteDb].} =
## executes the query and raises EDB if not successful. ## executes the query and raises EDB if not successful.
var q = dbFormat(query, args) var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db) if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
proc newRow(L: int): TRow = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
@ -103,8 +104,8 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
while mysql.fetchRow(sqlres) != nil: discard while mysql.fetchRow(sqlres) != nil: discard
mysql.freeResult(sqlres) mysql.freeResult(sqlres)
iterator fastRows*(db: TDbConn, query: TSqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For MySQL this is the case!. ## query, the results can be undefined. For MySQL this is the case!.
@ -126,10 +127,10 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
yield result yield result
properFreeResult(sqlres, row) properFreeResult(sqlres, row)
proc getRow*(db: TDbConn, query: TSqlQuery, proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## retrieves a single row. If the query doesn't return any rows, this proc ## retrieves a single row. If the query doesn't return any rows, this proc
## will return a TRow with empty strings for each column. ## will return a Row with empty strings for each column.
rawExec(db, query, args) rawExec(db, query, args)
var sqlres = mysql.useResult(db) var sqlres = mysql.useResult(db)
if sqlres != nil: if sqlres != nil:
@ -145,8 +146,8 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
add(result[i], row[i]) add(result[i], row[i])
properFreeResult(sqlres, row) properFreeResult(sqlres, row)
proc getAllRows*(db: TDbConn, query: TSqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
## executes the query and returns the whole result dataset. ## executes the query and returns the whole result dataset.
result = @[] result = @[]
rawExec(db, query, args) rawExec(db, query, args)
@ -168,12 +169,12 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery,
inc(j) inc(j)
mysql.freeResult(sqlres) mysql.freeResult(sqlres)
iterator rows*(db: TDbConn, query: TSqlQuery, iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## same as `fastRows`, but slower and safe. ## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: TDbConn, query: TSqlQuery, proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} = args: varargs[string, `$`]): string {.tags: [FReadDB].} =
## executes the query and returns the first column of the first row of the ## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database ## result dataset. Returns "" if the dataset contains no rows or the database
@ -183,7 +184,7 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
result = row[0] result = row[0]
break break
proc tryInsertId*(db: TDbConn, query: TSqlQuery, proc tryInsertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. ## generated ID for the row or -1 in case of an error.
@ -193,14 +194,14 @@ proc tryInsertId*(db: TDbConn, query: TSqlQuery,
else: else:
result = mysql.insertId(db) result = mysql.insertId(db)
proc insertId*(db: TDbConn, query: TSqlQuery, proc insertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. ## generated ID for the row.
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: TDbConn, query: TSqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {. args: varargs[string, `$`]): int64 {.
tags: [FReadDB, FWriteDb].} = tags: [FReadDB, FWriteDb].} =
## runs the query (typically "UPDATE") and returns the ## runs the query (typically "UPDATE") and returns the
@ -208,11 +209,11 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
rawExec(db, query, args) rawExec(db, query, args)
result = mysql.affectedRows(db) result = mysql.affectedRows(db)
proc close*(db: TDbConn) {.tags: [FDb].} = proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection. ## closes the database connection.
if db != nil: mysql.close(db) if db != nil: mysql.close(db)
proc open*(connection, user, password, database: string): TDbConn {. proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} = tags: [FDb].} =
## opens a database connection. Raises `EDb` if the connection could not ## opens a database connection. Raises `EDb` if the connection could not
## be established. ## be established.
@ -230,8 +231,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
db_mysql.close(result) db_mysql.close(result)
dbError(errmsg) dbError(errmsg)
proc setEncoding*(connection: TDbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
result = mysql.set_character_set(connection, encoding) == 0 result = mysql.set_character_set(connection, encoding) == 0

View file

@ -13,28 +13,30 @@
import strutils, postgres import strutils, postgres
type type
TDbConn* = PPGconn ## encapsulates a database connection DbConn* = PPGconn ## encapsulates a database connection
TRow* = seq[string] ## a row of a dataset. NULL database values will be Row* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string. ## transformed always to the empty string.
EDb* = object of IOError ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
TSqlQuery* = distinct string ## an SQL query string SqlQuery* = distinct string ## an SQL query string
TSqlPrepared* = distinct string ## a identifier for the prepared queries SqlPrepared* = distinct string ## a identifier for the prepared queries
FDb* = object of IOEffect ## effect that denotes a database operation FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
TSqlPrepared: SqlPrepared].}
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a TSqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = TSqlQuery(query) result = SqlQuery(query)
proc dbError*(db: TDbConn) {.noreturn.} = proc dbError*(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
new(e) new(e)
@ -56,7 +58,7 @@ proc dbQuote*(s: string): string =
else: add(result, c) else: add(result, c)
add(result, '\'') add(result, '\'')
proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string = proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
result = "" result = ""
var a = 0 var a = 0
for c in items(string(formatstr)): for c in items(string(formatstr)):
@ -69,7 +71,7 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
else: else:
add(result, c) add(result, c)
proc tryExec*(db: TDbConn, query: TSqlQuery, proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} = args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
var arr = allocCStringArray(args) var arr = allocCStringArray(args)
@ -79,7 +81,7 @@ proc tryExec*(db: TDbConn, query: TSqlQuery,
result = pqresultStatus(res) == PGRES_COMMAND_OK result = pqresultStatus(res) == PGRES_COMMAND_OK
pqclear(res) pqclear(res)
proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {. proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDB, FWriteDb].} = tags: [FReadDB, FWriteDb].} =
## executes the query and raises EDB if not successful. ## executes the query and raises EDB if not successful.
var arr = allocCStringArray(args) var arr = allocCStringArray(args)
@ -89,7 +91,7 @@ proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
pqclear(res) pqclear(res)
proc exec*(db: TDbConn, stmtName: TSqlPrepared, proc exec*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string]) {.tags: [FReadDB, FWriteDb].} = args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
var arr = allocCStringArray(args) var arr = allocCStringArray(args)
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr, var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
@ -98,11 +100,11 @@ proc exec*(db: TDbConn, stmtName: TSqlPrepared,
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
pqclear(res) pqclear(res)
proc newRow(L: int): TRow = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
proc setupQuery(db: TDbConn, query: TSqlQuery, proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): PPGresult = args: varargs[string]): PPGresult =
var arr = allocCStringArray(args) var arr = allocCStringArray(args)
result = pqexecParams(db, query.string, int32(args.len), nil, arr, result = pqexecParams(db, query.string, int32(args.len), nil, arr,
@ -110,7 +112,7 @@ proc setupQuery(db: TDbConn, query: TSqlQuery,
deallocCStringArray(arr) deallocCStringArray(arr)
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
proc setupQuery(db: TDbConn, stmtName: TSqlPrepared, proc setupQuery(db: DbConn, stmtName: SqlPrepared,
args: varargs[string]): PPGresult = args: varargs[string]): PPGresult =
var arr = allocCStringArray(args) var arr = allocCStringArray(args)
result = pqexecPrepared(db, stmtName.string, int32(args.len), arr, result = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
@ -118,13 +120,13 @@ proc setupQuery(db: TDbConn, stmtName: TSqlPrepared,
deallocCStringArray(arr) deallocCStringArray(arr)
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
proc prepare*(db: TDbConn; stmtName: string, query: TSqlQuery; proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
nParams: int): TSqlPrepared = nParams: int): SqlPrepared =
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil) var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
return TSqlPrepared(stmtName) return SqlPrepared(stmtName)
proc setRow(res: PPGresult, r: var TRow, line, cols: int32) = proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
for col in 0..cols-1: for col in 0..cols-1:
setLen(r[col], 0) setLen(r[col], 0)
let x = pqgetvalue(res, line, col) let x = pqgetvalue(res, line, col)
@ -133,8 +135,8 @@ proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
else: else:
add(r[col], x) add(r[col], x)
iterator fastRows*(db: TDbConn, query: TSqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Postgres it is safe though. ## query, the results can be undefined. For Postgres it is safe though.
@ -146,8 +148,8 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
yield result yield result
pqclear(res) pqclear(res)
iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared, iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the prepared query and iterates over the result dataset. ## executes the prepared query and iterates over the result dataset.
var res = setupQuery(db, stmtName, args) var res = setupQuery(db, stmtName, args)
var L = pqNfields(res) var L = pqNfields(res)
@ -157,44 +159,44 @@ iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
yield result yield result
pqClear(res) pqClear(res)
proc getRow*(db: TDbConn, query: TSqlQuery, proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## retrieves a single row. If the query doesn't return any rows, this proc ## retrieves a single row. If the query doesn't return any rows, this proc
## will return a TRow with empty strings for each column. ## will return a Row with empty strings for each column.
var res = setupQuery(db, query, args) var res = setupQuery(db, query, args)
var L = pqnfields(res) var L = pqnfields(res)
result = newRow(L) result = newRow(L)
setRow(res, result, 0, L) setRow(res, result, 0, L)
pqclear(res) pqclear(res)
proc getRow*(db: TDbConn, stmtName: TSqlPrepared, proc getRow*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
var res = setupQuery(db, stmtName, args) var res = setupQuery(db, stmtName, args)
var L = pqNfields(res) var L = pqNfields(res)
result = newRow(L) result = newRow(L)
setRow(res, result, 0, L) setRow(res, result, 0, L)
pqClear(res) pqClear(res)
proc getAllRows*(db: TDbConn, query: TSqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
## executes the query and returns the whole result dataset. ## executes the query and returns the whole result dataset.
result = @[] result = @[]
for r in fastRows(db, query, args): for r in fastRows(db, query, args):
result.add(r) result.add(r)
proc getAllRows*(db: TDbConn, stmtName: TSqlPrepared, proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
## executes the prepared query and returns the whole result dataset. ## executes the prepared query and returns the whole result dataset.
result = @[] result = @[]
for r in fastRows(db, stmtName, args): for r in fastRows(db, stmtName, args):
result.add(r) result.add(r)
iterator rows*(db: TDbConn, query: TSqlQuery, iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## same as `fastRows`, but slower and safe. ## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: TDbConn, query: TSqlQuery, proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} = args: varargs[string, `$`]): string {.tags: [FReadDB].} =
## executes the query and returns the first column of the first row of the ## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database ## result dataset. Returns "" if the dataset contains no rows or the database
@ -202,20 +204,20 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
var x = pqgetvalue(setupQuery(db, query, args), 0, 0) var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
result = if isNil(x): "" else: $x result = if isNil(x): "" else: $x
proc tryInsertID*(db: TDbConn, query: TSqlQuery, proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}= args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. For Postgre this adds ## generated ID for the row or -1 in case of an error. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is ## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``. ## named ``id``.
var x = pqgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"), var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
args), 0, 0) args), 0, 0)
if not isNil(x): if not isNil(x):
result = parseBiggestInt($x) result = parseBiggestInt($x)
else: else:
result = -1 result = -1
proc insertID*(db: TDbConn, query: TSqlQuery, proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds ## generated ID for the row. For Postgre this adds
@ -224,7 +226,7 @@ proc insertID*(db: TDbConn, query: TSqlQuery,
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: TDbConn, query: TSqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [ args: varargs[string, `$`]): int64 {.tags: [
FReadDB, FWriteDb].} = FReadDB, FWriteDb].} =
## executes the query (typically "UPDATE") and returns the ## executes the query (typically "UPDATE") and returns the
@ -235,11 +237,11 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
result = parseBiggestInt($pqcmdTuples(res)) result = parseBiggestInt($pqcmdTuples(res))
pqclear(res) pqclear(res)
proc close*(db: TDbConn) {.tags: [FDb].} = proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection. ## closes the database connection.
if db != nil: pqfinish(db) if db != nil: pqfinish(db)
proc open*(connection, user, password, database: string): TDbConn {. proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} = tags: [FDb].} =
## opens a database connection. Raises `EDb` if the connection could not ## opens a database connection. Raises `EDb` if the connection could not
## be established. ## be established.
@ -261,8 +263,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
result = pqsetdbLogin(nil, nil, nil, nil, database, user, password) result = pqsetdbLogin(nil, nil, nil, nil, database, user, password)
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
proc setEncoding*(connection: TDbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
return pqsetClientEncoding(connection, encoding) == 0 return pqsetClientEncoding(connection, encoding) == 0

View file

@ -13,27 +13,28 @@
import strutils, sqlite3 import strutils, sqlite3
type type
TDbConn* = PSqlite3 ## encapsulates a database connection DbConn* = PSqlite3 ## encapsulates a database connection
TRow* = seq[string] ## a row of a dataset. NULL database values will be Row* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string. ## transformed always to the empty string.
EDb* = object of IOError ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
TSqlQuery* = distinct string ## an SQL query string SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a TSqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = TSqlQuery(query) result = SqlQuery(query)
proc dbError(db: TDbConn) {.noreturn.} = proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
new(e) new(e)
@ -55,7 +56,7 @@ proc dbQuote(s: string): string =
else: add(result, c) else: add(result, c)
add(result, '\'') add(result, '\'')
proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string = proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
result = "" result = ""
var a = 0 var a = 0
for c in items(string(formatstr)): for c in items(string(formatstr)):
@ -65,7 +66,7 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
else: else:
add(result, c) add(result, c)
proc tryExec*(db: TDbConn, query: TSqlQuery, proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} = args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args) var q = dbFormat(query, args)
@ -74,29 +75,29 @@ proc tryExec*(db: TDbConn, query: TSqlQuery,
if step(stmt) == SQLITE_DONE: if step(stmt) == SQLITE_DONE:
result = finalize(stmt) == SQLITE_OK result = finalize(stmt) == SQLITE_OK
proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {. proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDb, FWriteDb].} = tags: [FReadDb, FWriteDb].} =
## executes the query and raises EDB if not successful. ## executes the query and raises EDB if not successful.
if not tryExec(db, query, args): dbError(db) if not tryExec(db, query, args): dbError(db)
proc newRow(L: int): TRow = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
proc setupQuery(db: TDbConn, query: TSqlQuery, proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt = args: varargs[string]): Pstmt =
var q = dbFormat(query, args) var q = dbFormat(query, args)
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db) if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
proc setRow(stmt: Pstmt, r: var TRow, cols: cint) = proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
for col in 0..cols-1: for col in 0..cols-1:
setLen(r[col], column_bytes(stmt, col)) # set capacity setLen(r[col], column_bytes(stmt, col)) # set capacity
setLen(r[col], 0) setLen(r[col], 0)
let x = column_text(stmt, col) let x = column_text(stmt, col)
if not isNil(x): add(r[col], x) if not isNil(x): add(r[col], x)
iterator fastRows*(db: TDbConn, query: TSqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Sqlite it is safe though. ## query, the results can be undefined. For Sqlite it is safe though.
@ -108,10 +109,10 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
yield result yield result
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc getRow*(db: TDbConn, query: TSqlQuery, proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## retrieves a single row. If the query doesn't return any rows, this proc ## retrieves a single row. If the query doesn't return any rows, this proc
## will return a TRow with empty strings for each column. ## will return a Row with empty strings for each column.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
var L = (column_count(stmt)) var L = (column_count(stmt))
result = newRow(L) result = newRow(L)
@ -119,19 +120,19 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
setRow(stmt, result, L) setRow(stmt, result, L)
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc getAllRows*(db: TDbConn, query: TSqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDb].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
## executes the query and returns the whole result dataset. ## executes the query and returns the whole result dataset.
result = @[] result = @[]
for r in fastRows(db, query, args): for r in fastRows(db, query, args):
result.add(r) result.add(r)
iterator rows*(db: TDbConn, query: TSqlQuery, iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## same as `FastRows`, but slower and safe. ## same as `FastRows`, but slower and safe.
for r in fastRows(db, query, args): yield r for r in fastRows(db, query, args): yield r
proc getValue*(db: TDbConn, query: TSqlQuery, proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDb].} = args: varargs[string, `$`]): string {.tags: [FReadDb].} =
## executes the query and returns the first column of the first row of the ## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database ## result dataset. Returns "" if the dataset contains no rows or the database
@ -148,7 +149,7 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
result = "" result = ""
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: TDbConn, query: TSqlQuery, proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} = {.tags: [FWriteDb], raises: [].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
@ -162,7 +163,7 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery,
if finalize(stmt) != SQLITE_OK: if finalize(stmt) != SQLITE_OK:
result = -1 result = -1
proc insertID*(db: TDbConn, query: TSqlQuery, proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds ## generated ID for the row. For Postgre this adds
@ -171,7 +172,7 @@ proc insertID*(db: TDbConn, query: TSqlQuery,
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: TDbConn, query: TSqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {. args: varargs[string, `$`]): int64 {.
tags: [FReadDb, FWriteDb].} = tags: [FReadDb, FWriteDb].} =
## executes the query (typically "UPDATE") and returns the ## executes the query (typically "UPDATE") and returns the
@ -179,21 +180,21 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
exec(db, query, args) exec(db, query, args)
result = changes(db) result = changes(db)
proc close*(db: TDbConn) {.tags: [FDb].} = proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection. ## closes the database connection.
if sqlite3.close(db) != SQLITE_OK: dbError(db) if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): TDbConn {. proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} = tags: [FDb].} =
## opens a database connection. Raises `EDb` if the connection could not ## opens a database connection. Raises `EDb` if the connection could not
## be established. Only the ``connection`` parameter is used for ``sqlite``. ## be established. Only the ``connection`` parameter is used for ``sqlite``.
var db: TDbConn var db: DbConn
if sqlite3.open(connection, db) == SQLITE_OK: if sqlite3.open(connection, db) == SQLITE_OK:
result = db result = db
else: else:
dbError(db) dbError(db)
proc setEncoding*(connection: TDbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.

View file

@ -17,23 +17,24 @@ from sdl import PSurface # Bug
from sdl_ttf import openFont, closeFont from sdl_ttf import openFont, closeFont
type type
TRect* = tuple[x, y, width, height: int] Rect* = tuple[x, y, width, height: int]
TPoint* = tuple[x, y: int] Point* = tuple[x, y: int]
PSurface* = ref TSurface ## a surface to draw onto PSurface* = ref Surface ## a surface to draw onto
TSurface* {.pure, final.} = object Surface* {.pure, final.} = object
w*, h*: Natural w*, h*: Natural
s*: sdl.PSurface s*: sdl.PSurface
EGraphics* = object of IOError EGraphics* = object of IOError
TFont {.pure, final.} = object Font {.pure, final.} = object
f: sdl_ttf.PFont f: sdl_ttf.PFont
color: sdl.TColor color: sdl.Color
PFont* = ref TFont ## represents a font PFont* = ref Font ## represents a font
{.deprecated: [TSurface: Surface, TFont: Font, TRect: Rect, TPoint: Point].}
proc toSdlColor*(c: Color): sdl.TColor = proc toSdlColor*(c: Color): sdl.Color =
## Convert colors.TColor to sdl.TColor ## Convert colors.Color to sdl.Color
var x = c.extractRGB var x = c.extractRGB
result.r = x.r and 0xff result.r = x.r and 0xff
result.g = x.g and 0xff result.g = x.g and 0xff
@ -45,8 +46,8 @@ proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff, return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
x.b and 0xff, alpha and 0xff) x.b and 0xff, alpha and 0xff)
proc toSdlRect*(r: TRect): sdl.TRect = proc toSdlRect*(r: Rect): sdl.Rect =
## Convert ``graphics.TRect`` to ``sdl.TRect``. ## Convert ``graphics.Rect`` to ``sdl.Rect``.
result.x = int16(r.x) result.x = int16(r.x)
result.y = int16(r.y) result.y = int16(r.y)
result.w = uint16(r.width) result.w = uint16(r.width)
@ -103,8 +104,9 @@ proc writeToBMP*(sur: PSurface, filename: string) =
raise newException(IOError, "cannot write: " & filename) raise newException(IOError, "cannot write: " & filename)
type type
TPixels = array[0..1000_000-1, int32] Pixels = array[0..1000_000-1, int32]
PPixels = ptr TPixels PPixels = ptr Pixels
{.deprecated: [TPixels: Pixels].}
template setPix(video, pitch, x, y, col: expr): stmt = template setPix(video, pitch, x, y, col: expr): stmt =
video[y * pitch + x] = int32(col) video[y * pitch + x] = int32(col)
@ -128,7 +130,7 @@ proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
#pixs[y * (sur.s.pitch div colSize) + x] = int(col) #pixs[y * (sur.s.pitch div colSize) + x] = int(col)
setPix(pixs, sur.s.pitch.int div ColSize, x, y, col) setPix(pixs, sur.s.pitch.int div ColSize, x, y, col)
proc `[]`*(sur: PSurface, p: TPoint): Color = proc `[]`*(sur: PSurface, p: Point): Color =
## get pixel at position `p`. No range checking is done! ## get pixel at position `p`. No range checking is done!
result = getPixel(sur, p.x, p.y) result = getPixel(sur, p.x, p.y)
@ -136,7 +138,7 @@ proc `[]`*(sur: PSurface, x, y: int): Color =
## get pixel at position ``(x, y)``. No range checking is done! ## get pixel at position ``(x, y)``. No range checking is done!
result = getPixel(sur, x, y) result = getPixel(sur, x, y)
proc `[]=`*(sur: PSurface, p: TPoint, col: Color) = proc `[]=`*(sur: PSurface, p: Point, col: Color) =
## set the pixel at position `p`. No range checking is done! ## set the pixel at position `p`. No range checking is done!
setPixel(sur, p.x, p.y, col) setPixel(sur, p.x, p.y, col)
@ -144,10 +146,10 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
## set the pixel at position ``(x, y)``. No range checking is done! ## set the pixel at position ``(x, y)``. No range checking is done!
setPixel(sur, x, y, col) setPixel(sur, x, y, col)
proc blit*(destSurf: PSurface, destRect: TRect, srcSurf: PSurface, proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
srcRect: TRect) = srcRect: Rect) =
## Copies ``srcSurf`` into ``destSurf`` ## Copies ``srcSurf`` into ``destSurf``
var destTRect, srcTRect: sdl.TRect var destTRect, srcTRect: sdl.Rect
destTRect.x = int16(destRect.x) destTRect.x = int16(destRect.x)
destTRect.y = int16(destRect.y) destTRect.y = int16(destRect.y)
@ -168,7 +170,7 @@ proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] =
result.width = int(w) result.width = int(w)
result.height = int(h) result.height = int(h)
proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) = proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
## Draws text with a transparent background, at location ``p`` with the given ## Draws text with a transparent background, at location ``p`` with the given
## font. ## font.
var textSur: PSurface # This surface will have the text drawn on it var textSur: PSurface # This surface will have the text drawn on it
@ -179,7 +181,7 @@ proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) =
# Merge the text surface with sur # Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawText*(sur: PSurface, p: TPoint, text: string, proc drawText*(sur: PSurface, p: Point, text: string,
bg: Color, font = defaultFont) = bg: Color, font = defaultFont) =
## Draws text, at location ``p`` with font ``font``. ``bg`` ## Draws text, at location ``p`` with font ``font``. ``bg``
## is the background color. ## is the background color.
@ -189,7 +191,7 @@ proc drawText*(sur: PSurface, p: TPoint, text: string,
# Merge the text surface with sur # Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawCircle*(sur: PSurface, p: TPoint, r: Natural, color: Color) = proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color ## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur`. ## onto the surface `sur`.
var video = cast[PPixels](sur.s.pixels) var video = cast[PPixels](sur.s.pixels)
@ -229,7 +231,7 @@ proc `>-<`(val: int, s: PSurface): int {.inline.} =
proc `>|<`(val: int, s: PSurface): int {.inline.} = proc `>|<`(val: int, s: PSurface): int {.inline.} =
return if val < 0: 0 elif val >= s.h: s.h-1 else: val return if val < 0: 0 elif val >= s.h: s.h-1 else: val
proc drawLine*(sur: PSurface, p1, p2: TPoint, color: Color) = proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
## draws a line between the two points `p1` and `p2` with the given color ## draws a line between the two points `p1` and `p2` with the given color
## onto the surface `sur`. ## onto the surface `sur`.
var stepx, stepy: int = 0 var stepx, stepy: int = 0
@ -291,7 +293,7 @@ proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) =
for i in 0 .. min(sur.s.h-y, h)-1: for i in 0 .. min(sur.s.h-y, h)-1:
setPix(video, pitch, x, y + i, color) setPix(video, pitch, x, y + i, color)
proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) = proc fillCircle*(s: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color ## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur` and fills it. ## onto the surface `sur` and fills it.
var a = 1 - r var a = 1 - r
@ -319,7 +321,7 @@ proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) =
drawVerLine(s, x - py - 1, y - px, px, color) drawVerLine(s, x - py - 1, y - px, px, color)
px = px + 1 px = px + 1
proc drawRect*(sur: PSurface, r: TRect, color: Color) = proc drawRect*(sur: PSurface, r: Rect, color: Color) =
## draws a rectangle. ## draws a rectangle.
var video = cast[PPixels](sur.s.pixels) var video = cast[PPixels](sur.s.pixels)
var pitch = sur.s.pitch.int div ColSize var pitch = sur.s.pitch.int div ColSize
@ -337,7 +339,7 @@ proc drawRect*(sur: PSurface, r: TRect, color: Color) =
setPix(video, pitch, r.x, r.y + i, color) setPix(video, pitch, r.x, r.y + i, color)
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
proc fillRect*(sur: PSurface, r: TRect, col: Color) = proc fillRect*(sur: PSurface, r: Rect, col: Color) =
## Fills a rectangle using sdl's ``FillRect`` function. ## Fills a rectangle using sdl's ``FillRect`` function.
var rect = toSdlRect(r) var rect = toSdlRect(r)
if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1: if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1:
@ -424,7 +426,7 @@ template cround(x: expr): expr = ipart(x + 0.5)
template fpart(x: expr): expr = x - ipart(x) template fpart(x: expr): expr = x - ipart(x)
template rfpart(x: expr): expr = 1.0 - fpart(x) template rfpart(x: expr): expr = 1.0 - fpart(x)
proc drawLineAA*(sur: PSurface, p1, p2: TPoint, color: Color) = proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's ## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
## line algorithm ## line algorithm
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(), var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
@ -490,9 +492,9 @@ proc fillSurface*(sur: PSurface, color: Color) =
template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {. template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
immediate.} = immediate.} =
## Simple template which creates an event loop. ``Event`` is the name of the ## Simple template which creates an event loop. ``Event`` is the name of the
## variable containing the TEvent object. ## variable containing the Event object.
while true: while true:
var event: sdl.TEvent var event: sdl.Event
if sdl.waitEvent(addr(event)) == 1: if sdl.waitEvent(addr(event)) == 1:
actions actions

View file

@ -135,7 +135,7 @@ else:
var cur, old: Termios var cur, old: Termios
discard fd.tcgetattr(cur.addr) discard fd.tcgetattr(cur.addr)
old = cur old = cur
cur.c_lflag = cur.c_lflag and not Tcflag(ECHO) cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
discard fd.tcsetattr(TCSADRAIN, cur.addr) discard fd.tcsetattr(TCSADRAIN, cur.addr)
stdout.write prompt stdout.write prompt
result = stdin.readLine(password) result = stdin.readLine(password)

View file

@ -15,11 +15,12 @@
import openssl, strutils, os import openssl, strutils, os
type type
TSecureSocket* = object SecureSocket* = object
ssl: SslPtr ssl: SslPtr
bio: BIO bio: BIO
{.deprecated: [TSecureSocket: SecureSocket].}
proc connect*(sock: var TSecureSocket, address: string, proc connect*(sock: var SecureSocket, address: string,
port: int): int = port: int): int =
## Connects to the specified `address` on the specified `port`. ## Connects to the specified `address` on the specified `port`.
## Returns the result of the certificate validation. ## Returns the result of the certificate validation.
@ -52,7 +53,7 @@ proc connect*(sock: var TSecureSocket, address: string,
result = SSL_get_verify_result(sock.ssl) result = SSL_get_verify_result(sock.ssl)
proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool = proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
## Acts in a similar fashion to the `recvLine` in the sockets module. ## Acts in a similar fashion to the `recvLine` in the sockets module.
## Returns false when no data is available to be read. ## Returns false when no data is available to be read.
## `Line` must be initialized and not nil! ## `Line` must be initialized and not nil!
@ -71,19 +72,19 @@ proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
add(line.string, c) add(line.string, c)
proc send*(sock: TSecureSocket, data: string) = proc send*(sock: SecureSocket, data: string) =
## Writes `data` to the socket. ## Writes `data` to the socket.
if BIO_write(sock.bio, data, data.len.cint) <= 0: if BIO_write(sock.bio, data, data.len.cint) <= 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
proc close*(sock: TSecureSocket) = proc close*(sock: SecureSocket) =
## Closes the socket ## Closes the socket
if BIO_free(sock.bio) <= 0: if BIO_free(sock.bio) <= 0:
ERR_print_errors_fp(stderr) ERR_print_errors_fp(stderr)
raiseOSError(osLastError()) raiseOSError(osLastError())
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
var s: TSecureSocket var s: SecureSocket
echo connect(s, "smtp.gmail.com", 465) echo connect(s, "smtp.gmail.com", 465)
#var buffer: array[0..255, char] #var buffer: array[0..255, char]

View file

@ -13,18 +13,18 @@ import
streams, libzip, times, os, strutils streams, libzip, times, os, strutils
type type
TZipArchive* = object of RootObj ## represents a zip archive ZipArchive* = object of RootObj ## represents a zip archive
mode: FileMode mode: FileMode
w: PZip w: PZip
{.deprecated: [TZipArchive: ZipArchive].}
proc zipError(z: var ZipArchive) =
proc zipError(z: var TZipArchive) =
var e: ref IOError var e: ref IOError
new(e) new(e)
e.msg = $zip_strerror(z.w) e.msg = $zip_strerror(z.w)
raise e raise e
proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool = proc open*(z: var ZipArchive, filename: string, mode: FileMode = fmRead): bool =
## Opens a zip file for reading, writing or appending. All file modes are ## Opens a zip file for reading, writing or appending. All file modes are
## supported. Returns true iff successful, false otherwise. ## supported. Returns true iff successful, false otherwise.
var err, flags: int32 var err, flags: int32
@ -38,11 +38,11 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool
z.mode = mode z.mode = mode
result = z.w != nil result = z.w != nil
proc close*(z: var TZipArchive) = proc close*(z: var ZipArchive) =
## Closes a zip file. ## Closes a zip file.
zip_close(z.w) zip_close(z.w)
proc createDir*(z: var TZipArchive, dir: string) = proc createDir*(z: var ZipArchive, dir: string) =
## Creates a directory within the `z` archive. This does not fail if the ## Creates a directory within the `z` archive. This does not fail if the
## directory already exists. Note that for adding a file like ## directory already exists. Note that for adding a file like
## ``"path1/path2/filename"`` it is not necessary ## ``"path1/path2/filename"`` it is not necessary
@ -52,7 +52,7 @@ proc createDir*(z: var TZipArchive, dir: string) =
discard zip_add_dir(z.w, dir) discard zip_add_dir(z.w, dir)
zip_error_clear(z.w) zip_error_clear(z.w)
proc addFile*(z: var TZipArchive, dest, src: string) = proc addFile*(z: var ZipArchive, dest, src: string) =
## Adds the file `src` to the archive `z` with the name `dest`. `dest` ## Adds the file `src` to the archive `z` with the name `dest`. `dest`
## may contain a path that will be created. ## may contain a path that will be created.
assert(z.mode != fmRead) assert(z.mode != fmRead)
@ -67,13 +67,13 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
zip_source_free(zipsrc) zip_source_free(zipsrc)
zipError(z) zipError(z)
proc addFile*(z: var TZipArchive, file: string) = proc addFile*(z: var ZipArchive, file: string) =
## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is ## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
## the name of the destination. ## the name of the destination.
addFile(z, file, file) addFile(z, file, file)
proc mySourceCallback(state, data: pointer, len: int, proc mySourceCallback(state, data: pointer, len: int,
cmd: TZipSourceCmd): int {.cdecl.} = cmd: ZipSourceCmd): int {.cdecl.} =
var src = cast[Stream](state) var src = cast[Stream](state)
case cmd case cmd
of ZIP_SOURCE_OPEN: of ZIP_SOURCE_OPEN:
@ -86,7 +86,7 @@ proc mySourceCallback(state, data: pointer, len: int,
zip_stat_init(stat) zip_stat_init(stat)
stat.size = high(int32)-1 # we don't know the size stat.size = high(int32)-1 # we don't know the size
stat.mtime = getTime() stat.mtime = getTime()
result = sizeof(TZipStat) result = sizeof(ZipStat)
of ZIP_SOURCE_ERROR: of ZIP_SOURCE_ERROR:
var err = cast[ptr array[0..1, cint]](data) var err = cast[ptr array[0..1, cint]](data)
err[0] = ZIP_ER_INTERNAL err[0] = ZIP_ER_INTERNAL
@ -95,7 +95,7 @@ proc mySourceCallback(state, data: pointer, len: int,
of constZIP_SOURCE_FREE: GC_unref(src) of constZIP_SOURCE_FREE: GC_unref(src)
else: assert(false) else: assert(false)
proc addFile*(z: var TZipArchive, dest: string, src: Stream) = proc addFile*(z: var ZipArchive, dest: string, src: Stream) =
## Adds a file named with `dest` to the archive `z`. `dest` ## Adds a file named with `dest` to the archive `z`. `dest`
## may contain a path. The file's content is read from the `src` stream. ## may contain a path. The file's content is read from the `src` stream.
assert(z.mode != fmRead) assert(z.mode != fmRead)
@ -114,7 +114,7 @@ type
atEnd: bool atEnd: bool
PZipFileStream* = PZipFileStream* =
ref TZipFileStream ## a reader stream of a file within a zip archive ref ZipFileStream ## a reader stream of a file within a zip archive
proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f) proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f)
proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd
@ -134,7 +134,7 @@ proc newZipFileStream(f: PZipFile): PZipFileStream =
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc getStream*(z: var TZipArchive, filename: string): PZipFileStream = proc getStream*(z: var ZipArchive, filename: string): PZipFileStream =
## returns a stream that can be used to read the file named `filename` ## returns a stream that can be used to read the file named `filename`
## from the archive `z`. Returns nil in case of an error. ## from the archive `z`. Returns nil in case of an error.
## The returned stream does not support the `setPosition`, `getPosition`, ## The returned stream does not support the `setPosition`, `getPosition`,
@ -142,7 +142,7 @@ proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
var x = zip_fopen(z.w, filename, 0'i32) var x = zip_fopen(z.w, filename, 0'i32)
if x != nil: result = newZipFileStream(x) if x != nil: result = newZipFileStream(x)
iterator walkFiles*(z: var TZipArchive): string = iterator walkFiles*(z: var ZipArchive): string =
## walks over all files in the archive `z` and returns the filename ## walks over all files in the archive `z` and returns the filename
## (including the path). ## (including the path).
var i = 0'i32 var i = 0'i32
@ -152,7 +152,7 @@ iterator walkFiles*(z: var TZipArchive): string =
inc(i) inc(i)
proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) = proc extractFile*(z: var ZipArchive, srcFile: string, dest: Stream) =
## extracts a file from the zip archive `z` to the destination stream. ## extracts a file from the zip archive `z` to the destination stream.
var strm = getStream(z, srcFile) var strm = getStream(z, srcFile)
while true: while true:
@ -162,13 +162,13 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
dest.flush() dest.flush()
strm.close() strm.close()
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) = proc extractFile*(z: var ZipArchive, srcFile: string, dest: string) =
## extracts a file from the zip archive `z` to the destination filename. ## extracts a file from the zip archive `z` to the destination filename.
var file = newFileStream(dest, fmWrite) var file = newFileStream(dest, fmWrite)
extractFile(z, srcFile, file) extractFile(z, srcFile, file)
file.close() file.close()
proc extractAll*(z: var TZipArchive, dest: string) = proc extractAll*(z: var ZipArchive, dest: string) =
## extracts all files from archive `z` to the destination directory. ## extracts all files from archive `z` to the destination directory.
for file in walkFiles(z): for file in walkFiles(z):
if file.endsWith("/"): if file.endsWith("/"):
@ -177,7 +177,7 @@ proc extractAll*(z: var TZipArchive, dest: string) =
extractFile(z, file, dest / file) extractFile(z, file, dest / file)
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
var zip: TZipArchive var zip: ZipArchive
if not zip.open("nim-0.11.0.zip"): if not zip.open("nim-0.11.0.zip"):
raise newException(IOError, "opening zip failed") raise newException(IOError, "opening zip failed")
zip.extractAll("test") zip.extractAll("test")

View file

@ -15,7 +15,7 @@ import
strutils strutils
type type
TTokenClass* = enum TokenClass* = enum
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber, gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit, gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
@ -23,20 +23,22 @@ type
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler, gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel, gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
gtReference, gtOther gtReference, gtOther
TGeneralTokenizer* = object of RootObj GeneralTokenizer* = object of RootObj
kind*: TTokenClass kind*: TokenClass
start*, length*: int start*, length*: int
buf: cstring buf: cstring
pos: int pos: int
state: TTokenClass state: TokenClass
TSourceLanguage* = enum SourceLanguage* = enum
langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava
{.deprecated: [TSourceLanguage: SourceLanguage, TTokenClass: TokenClass,
TGeneralTokenizer: GeneralTokenizer].}
const const
sourceLanguageToStr*: array[TSourceLanguage, string] = ["none", sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
"Nim", "Nimrod", "C++", "C#", "C", "Java"] "Nim", "Nimrod", "C++", "C#", "C", "Java"]
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace", tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber", "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit", "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment", "EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
@ -58,29 +60,29 @@ const
"template", "try", "tuple", "type", "using", "var", "when", "while", "with", "template", "try", "tuple", "type", "using", "var", "when", "while", "with",
"without", "xor", "yield"] "without", "xor", "yield"]
proc getSourceLanguage*(name: string): TSourceLanguage = proc getSourceLanguage*(name: string): SourceLanguage =
for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)): for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
return i return i
result = langNone result = langNone
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) = proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) =
g.buf = buf g.buf = buf
g.kind = low(TTokenClass) g.kind = low(TokenClass)
g.start = 0 g.start = 0
g.length = 0 g.length = 0
g.state = low(TTokenClass) g.state = low(TokenClass)
var pos = 0 # skip initial whitespace: var pos = 0 # skip initial whitespace:
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
g.pos = pos g.pos = pos
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) =
initGeneralTokenizer(g, cstring(buf)) initGeneralTokenizer(g, cstring(buf))
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) = proc deinitGeneralTokenizer*(g: var GeneralTokenizer) =
discard discard
proc nimGetKeyword(id: string): TTokenClass = proc nimGetKeyword(id: string): TokenClass =
for k in nimKeywords: for k in nimKeywords:
if cmpIgnoreStyle(id, k) == 0: return gtKeyword if cmpIgnoreStyle(id, k) == 0: return gtKeyword
result = gtIdentifier result = gtIdentifier
@ -92,7 +94,7 @@ proc nimGetKeyword(id: string): TTokenClass =
else: else:
result = gtIdentifier result = gtIdentifier
proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int = proc nimNumberPostfix(g: var GeneralTokenizer, position: int): int =
var pos = position var pos = position
if g.buf[pos] == '\'': if g.buf[pos] == '\'':
inc(pos) inc(pos)
@ -110,7 +112,7 @@ proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
discard discard
result = pos result = pos
proc nimNumber(g: var TGeneralTokenizer, position: int): int = proc nimNumber(g: var GeneralTokenizer, position: int): int =
const decChars = {'0'..'9', '_'} const decChars = {'0'..'9', '_'}
var pos = position var pos = position
g.kind = gtDecNumber g.kind = gtDecNumber
@ -130,7 +132,7 @@ const
OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'} '|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
proc nimNextToken(g: var TGeneralTokenizer) = proc nimNextToken(g: var GeneralTokenizer) =
const const
hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'} hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
octChars = {'0'..'7', '_'} octChars = {'0'..'7', '_'}
@ -278,7 +280,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
assert false, "nimNextToken: produced an empty token" assert false, "nimNextToken: produced an empty token"
g.pos = pos g.pos = pos
proc generalNumber(g: var TGeneralTokenizer, position: int): int = proc generalNumber(g: var GeneralTokenizer, position: int): int =
const decChars = {'0'..'9'} const decChars = {'0'..'9'}
var pos = position var pos = position
g.kind = gtDecNumber g.kind = gtDecNumber
@ -294,7 +296,7 @@ proc generalNumber(g: var TGeneralTokenizer, position: int): int =
while g.buf[pos] in decChars: inc(pos) while g.buf[pos] in decChars: inc(pos)
result = pos result = pos
proc generalStrLit(g: var TGeneralTokenizer, position: int): int = proc generalStrLit(g: var GeneralTokenizer, position: int): int =
const const
decChars = {'0'..'9'} decChars = {'0'..'9'}
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'} hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
@ -355,12 +357,13 @@ proc isKeywordIgnoreCase(x: openArray[string], y: string): int =
result = - 1 result = - 1
type type
TTokenizerFlag = enum TokenizerFlag = enum
hasPreprocessor, hasNestedComments hasPreprocessor, hasNestedComments
TTokenizerFlags = set[TTokenizerFlag] TokenizerFlags = set[TokenizerFlag]
{.deprecated: [TTokenizerFlag: TokenizerFlag, TTokenizerFlags: TokenizerFlags].}
proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string], proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
flags: TTokenizerFlags) = flags: TokenizerFlags) =
const const
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'} hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
octChars = {'0'..'7'} octChars = {'0'..'7'}
@ -493,7 +496,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
assert false, "clikeNextToken: produced an empty token" assert false, "clikeNextToken: produced an empty token"
g.pos = pos g.pos = pos
proc cNextToken(g: var TGeneralTokenizer) = proc cNextToken(g: var GeneralTokenizer) =
const const
keywords: array[0..36, string] = ["_Bool", "_Complex", "_Imaginary", "auto", keywords: array[0..36, string] = ["_Bool", "_Complex", "_Imaginary", "auto",
"break", "case", "char", "const", "continue", "default", "do", "double", "break", "case", "char", "const", "continue", "default", "do", "double",
@ -503,7 +506,7 @@ proc cNextToken(g: var TGeneralTokenizer) =
"volatile", "while"] "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor}) clikeNextToken(g, keywords, {hasPreprocessor})
proc cppNextToken(g: var TGeneralTokenizer) = proc cppNextToken(g: var GeneralTokenizer) =
const const
keywords: array[0..47, string] = ["asm", "auto", "break", "case", "catch", keywords: array[0..47, string] = ["asm", "auto", "break", "case", "catch",
"char", "class", "const", "continue", "default", "delete", "do", "double", "char", "class", "const", "continue", "default", "delete", "do", "double",
@ -514,7 +517,7 @@ proc cppNextToken(g: var TGeneralTokenizer) =
"union", "unsigned", "virtual", "void", "volatile", "while"] "union", "unsigned", "virtual", "void", "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor}) clikeNextToken(g, keywords, {hasPreprocessor})
proc csharpNextToken(g: var TGeneralTokenizer) = proc csharpNextToken(g: var GeneralTokenizer) =
const const
keywords: array[0..76, string] = ["abstract", "as", "base", "bool", "break", keywords: array[0..76, string] = ["abstract", "as", "base", "bool", "break",
"byte", "case", "catch", "char", "checked", "class", "const", "continue", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
@ -529,7 +532,7 @@ proc csharpNextToken(g: var TGeneralTokenizer) =
"virtual", "void", "volatile", "while"] "virtual", "void", "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor}) clikeNextToken(g, keywords, {hasPreprocessor})
proc javaNextToken(g: var TGeneralTokenizer) = proc javaNextToken(g: var GeneralTokenizer) =
const const
keywords: array[0..52, string] = ["abstract", "assert", "boolean", "break", keywords: array[0..52, string] = ["abstract", "assert", "boolean", "break",
"byte", "case", "catch", "char", "class", "const", "continue", "default", "byte", "case", "catch", "char", "class", "const", "continue", "default",
@ -541,7 +544,7 @@ proc javaNextToken(g: var TGeneralTokenizer) =
"try", "void", "volatile", "while"] "try", "void", "volatile", "while"]
clikeNextToken(g, keywords, {}) clikeNextToken(g, keywords, {})
proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) = proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
case lang case lang
of langNone: assert false of langNone: assert false
of langNim, langNimrod: nimNextToken(g) of langNim, langNimrod: nimNextToken(g)

View file

@ -15,7 +15,7 @@ import
os, strutils, rstast os, strutils, rstast
type type
TRstParseOption* = enum ## options for the RST parser RstParseOption* = enum ## options for the RST parser
roSkipPounds, ## skip ``#`` at line beginning (documentation roSkipPounds, ## skip ``#`` at line beginning (documentation
## embedded in Nim comments) ## embedded in Nim comments)
roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportSmilies, ## make the RST parser support smilies like ``:)``
@ -23,14 +23,14 @@ type
## it for sandboxing) ## it for sandboxing)
roSupportMarkdown ## support additional features of markdown roSupportMarkdown ## support additional features of markdown
TRstParseOptions* = set[TRstParseOption] RstParseOptions* = set[RstParseOption]
TMsgClass* = enum MsgClass* = enum
mcHint = "Hint", mcHint = "Hint",
mcWarning = "Warning", mcWarning = "Warning",
mcError = "Error" mcError = "Error"
TMsgKind* = enum ## the possible messages MsgKind* = enum ## the possible messages
meCannotOpenFile, meCannotOpenFile,
meExpected, meExpected,
meGridTableNotImplemented, meGridTableNotImplemented,
@ -42,12 +42,14 @@ type
mwUnsupportedLanguage, mwUnsupportedLanguage,
mwUnsupportedField mwUnsupportedField
TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind, MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
arg: string) {.nimcall.} ## what to do in case of an error arg: string) {.nimcall.} ## what to do in case of an error
TFindFileHandler* = proc (filename: string): string {.nimcall.} FindFileHandler* = proc (filename: string): string {.nimcall.}
{.deprecated: [TRstParseOptions: RstParseOptions, TRstParseOption: RstParseOption,
TMsgKind: MsgKind].}
const const
messages: array [TMsgKind, string] = [ messages: array [MsgKind, string] = [
meCannotOpenFile: "cannot open '$1'", meCannotOpenFile: "cannot open '$1'",
meExpected: "'$1' expected", meExpected: "'$1' expected",
meGridTableNotImplemented: "grid table is not implemented", meGridTableNotImplemented: "grid table is not implemented",
@ -111,23 +113,24 @@ const
} }
type type
TTokType = enum TokType = enum
tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther
TToken = object # a RST token Token = object # a RST token
kind*: TTokType # the type of the token kind*: TokType # the type of the token
ival*: int # the indentation or parsed integer value ival*: int # the indentation or parsed integer value
symbol*: string # the parsed symbol as string symbol*: string # the parsed symbol as string
line*, col*: int # line and column of the token line*, col*: int # line and column of the token
TTokenSeq = seq[TToken] TokenSeq = seq[Token]
TLexer = object of RootObj Lexer = object of RootObj
buf*: cstring buf*: cstring
bufpos*: int bufpos*: int
line*, col*, baseIndent*: int line*, col*, baseIndent*: int
skipPounds*: bool skipPounds*: bool
{.deprecated: [TTokType: TokType, TToken: Token, TTokenSeq: TokenSeq,
TLexer: Lexer].}
proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
tok.kind = tkWord tok.kind = tkWord
tok.line = L.line tok.line = L.line
tok.col = L.col tok.col = L.col
@ -139,7 +142,7 @@ proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
inc(L.col, pos - L.bufpos) inc(L.col, pos - L.bufpos)
L.bufpos = pos L.bufpos = pos
proc getAdornment(L: var TLexer, tok: var TToken) = proc getAdornment(L: var Lexer, tok: var Token) =
tok.kind = tkAdornment tok.kind = tkAdornment
tok.line = L.line tok.line = L.line
tok.col = L.col tok.col = L.col
@ -152,7 +155,7 @@ proc getAdornment(L: var TLexer, tok: var TToken) =
inc(L.col, pos - L.bufpos) inc(L.col, pos - L.bufpos)
L.bufpos = pos L.bufpos = pos
proc getIndentAux(L: var TLexer, start: int): int = proc getIndentAux(L: var Lexer, start: int): int =
var pos = start var pos = start
var buf = L.buf var buf = L.buf
# skip the newline (but include it in the token!) # skip the newline (but include it in the token!)
@ -181,7 +184,7 @@ proc getIndentAux(L: var TLexer, start: int): int =
result = getIndentAux(L, pos) result = getIndentAux(L, pos)
L.bufpos = pos # no need to set back buf L.bufpos = pos # no need to set back buf
proc getIndent(L: var TLexer, tok: var TToken) = proc getIndent(L: var Lexer, tok: var Token) =
tok.col = 0 tok.col = 0
tok.kind = tkIndent # skip the newline (but include it in the token!) tok.kind = tkIndent # skip the newline (but include it in the token!)
tok.ival = getIndentAux(L, L.bufpos) tok.ival = getIndentAux(L, L.bufpos)
@ -191,7 +194,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
tok.ival = max(tok.ival - L.baseIndent, 0) tok.ival = max(tok.ival - L.baseIndent, 0)
tok.symbol = "\n" & spaces(tok.ival) tok.symbol = "\n" & spaces(tok.ival)
proc rawGetTok(L: var TLexer, tok: var TToken) = proc rawGetTok(L: var Lexer, tok: var Token) =
tok.symbol = "" tok.symbol = ""
tok.ival = 0 tok.ival = 0
var c = L.buf[L.bufpos] var c = L.buf[L.bufpos]
@ -222,8 +225,8 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
inc(L.col) inc(L.col)
tok.col = max(tok.col - L.baseIndent, 0) tok.col = max(tok.col - L.baseIndent, 0)
proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int = proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
var L: TLexer var L: Lexer
var length = len(tokens) var length = len(tokens)
L.buf = cstring(buffer) L.buf = cstring(buffer)
L.line = 0 # skip UTF-8 BOM L.line = 0 # skip UTF-8 BOM
@ -253,31 +256,31 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
tokens[0].kind = tkIndent tokens[0].kind = tkIndent
type type
TLevelMap = array[char, int] LevelMap = array[char, int]
TSubstitution = object Substitution = object
key*: string key*: string
value*: PRstNode value*: PRstNode
TSharedState = object SharedState = object
options: TRstParseOptions # parsing options options: RstParseOptions # parsing options
uLevel, oLevel: int # counters for the section levels uLevel, oLevel: int # counters for the section levels
subs: seq[TSubstitution] # substitutions subs: seq[Substitution] # substitutions
refs: seq[TSubstitution] # references refs: seq[Substitution] # references
underlineToLevel: TLevelMap # Saves for each possible title adornment underlineToLevel: LevelMap # Saves for each possible title adornment
# character its level in the # character its level in the
# current document. # current document.
# This is for single underline adornments. # This is for single underline adornments.
overlineToLevel: TLevelMap # Saves for each possible title adornment overlineToLevel: LevelMap # Saves for each possible title adornment
# character its level in the current # character its level in the current
# document. # document.
# This is for over-underline adornments. # This is for over-underline adornments.
msgHandler: TMsgHandler # How to handle errors. msgHandler: MsgHandler # How to handle errors.
findFile: TFindFileHandler # How to find files. findFile: FindFileHandler # How to find files.
PSharedState = ref TSharedState PSharedState = ref SharedState
TRstParser = object of RootObj RstParser = object of RootObj
idx*: int idx*: int
tok*: TTokenSeq tok*: TokenSeq
s*: PSharedState s*: PSharedState
indentStack*: seq[int] indentStack*: seq[int]
filename*: string filename*: string
@ -285,8 +288,12 @@ type
hasToc*: bool hasToc*: bool
EParseError* = object of ValueError EParseError* = object of ValueError
{.deprecated: [TLevelMap: LevelMap, TSubstitution: Substitution,
TSharedState: SharedState, TRstParser: RstParser,
TMsgHandler: MsgHandler, TFindFileHandler: FindFileHandler,
TMsgClass: MsgClass].}
proc whichMsgClass*(k: TMsgKind): TMsgClass = proc whichMsgClass*(k: MsgKind): MsgClass =
## returns which message class `k` belongs to. ## returns which message class `k` belongs to.
case ($k)[1] case ($k)[1]
of 'e', 'E': result = mcError of 'e', 'E': result = mcError
@ -294,7 +301,7 @@ proc whichMsgClass*(k: TMsgKind): TMsgClass =
of 'h', 'H': result = mcHint of 'h', 'H': result = mcHint
else: assert false, "msgkind does not fit naming scheme" else: assert false, "msgkind does not fit naming scheme"
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: TMsgKind, proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
arg: string) {.procvar.} = arg: string) {.procvar.} =
let mc = msgkind.whichMsgClass let mc = msgkind.whichMsgClass
let a = messages[msgkind] % arg let a = messages[msgkind] % arg
@ -306,9 +313,9 @@ proc defaultFindFile*(filename: string): string {.procvar.} =
if existsFile(filename): result = filename if existsFile(filename): result = filename
else: result = "" else: result = ""
proc newSharedState(options: TRstParseOptions, proc newSharedState(options: RstParseOptions,
findFile: TFindFileHandler, findFile: FindFileHandler,
msgHandler: TMsgHandler): PSharedState = msgHandler: MsgHandler): PSharedState =
new(result) new(result)
result.subs = @[] result.subs = @[]
result.refs = @[] result.refs = @[]
@ -316,34 +323,34 @@ proc newSharedState(options: TRstParseOptions,
result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
result.findFile = if not isNil(findFile): findFile else: defaultFindFile result.findFile = if not isNil(findFile): findFile else: defaultFindFile
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
p.col + p.tok[p.idx].col, msgKind, arg) p.col + p.tok[p.idx].col, msgKind, arg)
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) = proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
p.s.msgHandler(p.filename, p.line + line, p.s.msgHandler(p.filename, p.line + line,
p.col + col, msgKind, arg) p.col + col, msgKind, arg)
proc rstMessage(p: TRstParser, msgKind: TMsgKind) = proc rstMessage(p: RstParser, msgKind: MsgKind) =
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
p.col + p.tok[p.idx].col, msgKind, p.col + p.tok[p.idx].col, msgKind,
p.tok[p.idx].symbol) p.tok[p.idx].symbol)
when false: when false:
proc corrupt(p: TRstParser) = proc corrupt(p: RstParser) =
assert p.indentStack[0] == 0 assert p.indentStack[0] == 0
for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000 for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000
proc currInd(p: TRstParser): int = proc currInd(p: RstParser): int =
result = p.indentStack[high(p.indentStack)] result = p.indentStack[high(p.indentStack)]
proc pushInd(p: var TRstParser, ind: int) = proc pushInd(p: var RstParser, ind: int) =
add(p.indentStack, ind) add(p.indentStack, ind)
proc popInd(p: var TRstParser) = proc popInd(p: var RstParser) =
if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1) if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1)
proc initParser(p: var TRstParser, sharedState: PSharedState) = proc initParser(p: var RstParser, sharedState: PSharedState) =
p.indentStack = @[0] p.indentStack = @[0]
p.tok = @[] p.tok = @[]
p.idx = 0 p.idx = 0
@ -393,7 +400,7 @@ proc rstnodeToRefname(n: PRstNode): string =
var b = false var b = false
rstnodeToRefnameAux(n, result, b) rstnodeToRefnameAux(n, result, b)
proc findSub(p: var TRstParser, n: PRstNode): int = proc findSub(p: var RstParser, n: PRstNode): int =
var key = addNodes(n) var key = addNodes(n)
# the spec says: if no exact match, try one without case distinction: # the spec says: if no exact match, try one without case distinction:
for i in countup(0, high(p.s.subs)): for i in countup(0, high(p.s.subs)):
@ -404,7 +411,7 @@ proc findSub(p: var TRstParser, n: PRstNode): int =
return i return i
result = -1 result = -1
proc setSub(p: var TRstParser, key: string, value: PRstNode) = proc setSub(p: var RstParser, key: string, value: PRstNode) =
var length = len(p.s.subs) var length = len(p.s.subs)
for i in countup(0, length - 1): for i in countup(0, length - 1):
if key == p.s.subs[i].key: if key == p.s.subs[i].key:
@ -414,7 +421,7 @@ proc setSub(p: var TRstParser, key: string, value: PRstNode) =
p.s.subs[length].key = key p.s.subs[length].key = key
p.s.subs[length].value = value p.s.subs[length].value = value
proc setRef(p: var TRstParser, key: string, value: PRstNode) = proc setRef(p: var RstParser, key: string, value: PRstNode) =
var length = len(p.s.refs) var length = len(p.s.refs)
for i in countup(0, length - 1): for i in countup(0, length - 1):
if key == p.s.refs[i].key: if key == p.s.refs[i].key:
@ -427,15 +434,15 @@ proc setRef(p: var TRstParser, key: string, value: PRstNode) =
p.s.refs[length].key = key p.s.refs[length].key = key
p.s.refs[length].value = value p.s.refs[length].value = value
proc findRef(p: var TRstParser, key: string): PRstNode = proc findRef(p: var RstParser, key: string): PRstNode =
for i in countup(0, high(p.s.refs)): for i in countup(0, high(p.s.refs)):
if key == p.s.refs[i].key: if key == p.s.refs[i].key:
return p.s.refs[i].value return p.s.refs[i].value
proc newLeaf(p: var TRstParser): PRstNode = proc newLeaf(p: var RstParser): PRstNode =
result = newRstNode(rnLeaf, p.tok[p.idx].symbol) result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
proc getReferenceName(p: var TRstParser, endStr: string): PRstNode = proc getReferenceName(p: var RstParser, endStr: string): PRstNode =
var res = newRstNode(rnInner) var res = newRstNode(rnInner)
while true: while true:
case p.tok[p.idx].kind case p.tok[p.idx].kind
@ -453,17 +460,17 @@ proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
inc(p.idx) inc(p.idx)
result = res result = res
proc untilEol(p: var TRstParser): PRstNode = proc untilEol(p: var RstParser): PRstNode =
result = newRstNode(rnInner) result = newRstNode(rnInner)
while not (p.tok[p.idx].kind in {tkIndent, tkEof}): while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
add(result, newLeaf(p)) add(result, newLeaf(p))
inc(p.idx) inc(p.idx)
proc expect(p: var TRstParser, tok: string) = proc expect(p: var RstParser, tok: string) =
if p.tok[p.idx].symbol == tok: inc(p.idx) if p.tok[p.idx].symbol == tok: inc(p.idx)
else: rstMessage(p, meExpected, tok) else: rstMessage(p, meExpected, tok)
proc isInlineMarkupEnd(p: TRstParser, markup: string): bool = proc isInlineMarkupEnd(p: RstParser, markup: string): bool =
result = p.tok[p.idx].symbol == markup result = p.tok[p.idx].symbol == markup
if not result: if not result:
return # Rule 3: return # Rule 3:
@ -480,7 +487,7 @@ proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"): if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"):
result = false result = false
proc isInlineMarkupStart(p: TRstParser, markup: string): bool = proc isInlineMarkupStart(p: RstParser, markup: string): bool =
var d: char var d: char
result = p.tok[p.idx].symbol == markup result = p.tok[p.idx].symbol == markup
if not result: if not result:
@ -507,7 +514,7 @@ proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
else: d = '\0' else: d = '\0'
if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d
proc match(p: TRstParser, start: int, expr: string): bool = proc match(p: RstParser, start: int, expr: string): bool =
# regular expressions are: # regular expressions are:
# special char exact match # special char exact match
# 'w' tkWord # 'w' tkWord
@ -562,7 +569,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
for i in countup(0, sep - incr): add(a, n.sons[i]) for i in countup(0, sep - incr): add(a, n.sons[i])
for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i]) for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode = proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
result = n result = n
if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"): if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
inc(p.idx) inc(p.idx)
@ -606,7 +613,7 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol)) add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
inc(p.idx, 3) inc(p.idx, 3)
proc matchVerbatim(p: TRstParser, start: int, expr: string): int = proc matchVerbatim(p: RstParser, start: int, expr: string): int =
result = start result = start
var j = 0 var j = 0
while j < expr.len and result < p.tok.len and while j < expr.len and result < p.tok.len and
@ -615,7 +622,7 @@ proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
inc result inc result
if j < expr.len: result = 0 if j < expr.len: result = 0
proc parseSmiley(p: var TRstParser): PRstNode = proc parseSmiley(p: var RstParser): PRstNode =
if p.tok[p.idx].symbol[0] notin SmileyStartChars: return if p.tok[p.idx].symbol[0] notin SmileyStartChars: return
for key, val in items(Smilies): for key, val in items(Smilies):
let m = matchVerbatim(p, p.idx, key) let m = matchVerbatim(p, p.idx, key)
@ -631,12 +638,12 @@ when false:
'$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&', '$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&',
'\128'..'\255'} '\128'..'\255'}
proc isUrl(p: TRstParser, i: int): bool = proc isUrl(p: RstParser, i: int): bool =
result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and
(p.tok[i+3].kind == tkWord) and (p.tok[i+3].kind == tkWord) and
(p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"]) (p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"])
proc parseUrl(p: var TRstParser, father: PRstNode) = proc parseUrl(p: var RstParser, father: PRstNode) =
#if p.tok[p.idx].symbol[strStart] == '<': #if p.tok[p.idx].symbol[strStart] == '<':
if isUrl(p, p.idx): if isUrl(p, p.idx):
var n = newRstNode(rnStandaloneHyperlink) var n = newRstNode(rnStandaloneHyperlink)
@ -656,7 +663,7 @@ proc parseUrl(p: var TRstParser, father: PRstNode) =
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n) if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
add(father, n) add(father, n)
proc parseBackslash(p: var TRstParser, father: PRstNode) = proc parseBackslash(p: var RstParser, father: PRstNode) =
assert(p.tok[p.idx].kind == tkPunct) assert(p.tok[p.idx].kind == tkPunct)
if p.tok[p.idx].symbol == "\\\\": if p.tok[p.idx].symbol == "\\\\":
add(father, newRstNode(rnLeaf, "\\")) add(father, newRstNode(rnLeaf, "\\"))
@ -671,7 +678,7 @@ proc parseBackslash(p: var TRstParser, father: PRstNode) =
inc(p.idx) inc(p.idx)
when false: when false:
proc parseAdhoc(p: var TRstParser, father: PRstNode, verbatim: bool) = proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) =
if not verbatim and isURL(p, p.idx): if not verbatim and isURL(p, p.idx):
var n = newRstNode(rnStandaloneHyperlink) var n = newRstNode(rnStandaloneHyperlink)
while true: while true:
@ -694,7 +701,7 @@ when false:
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n) if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
add(father, n) add(father, n)
proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string, proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
interpretBackslash: bool) = interpretBackslash: bool) =
let let
line = p.tok[p.idx].line line = p.tok[p.idx].line
@ -725,7 +732,7 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
inc(p.idx) inc(p.idx)
else: rstMessage(p, meExpected, postfix, line, col) else: rstMessage(p, meExpected, postfix, line, col)
proc parseMarkdownCodeblock(p: var TRstParser): PRstNode = proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
var args = newRstNode(rnDirArg) var args = newRstNode(rnDirArg)
if p.tok[p.idx].kind == tkWord: if p.tok[p.idx].kind == tkWord:
add(args, newLeaf(p)) add(args, newLeaf(p))
@ -755,7 +762,7 @@ proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
add(result, nil) add(result, nil)
add(result, lb) add(result, lb)
proc parseInline(p: var TRstParser, father: PRstNode) = proc parseInline(p: var RstParser, father: PRstNode) =
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkPunct: of tkPunct:
if isInlineMarkupStart(p, "***"): if isInlineMarkupStart(p, "***"):
@ -810,7 +817,7 @@ proc parseInline(p: var TRstParser, father: PRstNode) =
inc(p.idx) inc(p.idx)
else: discard else: discard
proc getDirective(p: var TRstParser): string = proc getDirective(p: var RstParser): string =
if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord: if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord:
var j = p.idx var j = p.idx
inc(p.idx) inc(p.idx)
@ -830,7 +837,7 @@ proc getDirective(p: var TRstParser): string =
else: else:
result = "" result = ""
proc parseComment(p: var TRstParser): PRstNode = proc parseComment(p: var RstParser): PRstNode =
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkIndent, tkEof: of tkIndent, tkEof:
if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent: if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent:
@ -851,34 +858,35 @@ proc parseComment(p: var TRstParser): PRstNode =
result = nil result = nil
type type
TDirKind = enum # must be ordered alphabetically! DirKind = enum # must be ordered alphabetically!
dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents, dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
{.deprecated: [TDirKind: DirKind].}
const const
DirIds: array[0..12, string] = ["", "author", "authors", "code", DirIds: array[0..12, string] = ["", "author", "authors", "code",
"code-block", "container", "contents", "figure", "image", "include", "code-block", "container", "contents", "figure", "image", "include",
"index", "raw", "title"] "index", "raw", "title"]
proc getDirKind(s: string): TDirKind = proc getDirKind(s: string): DirKind =
let i = find(DirIds, s) let i = find(DirIds, s)
if i >= 0: result = TDirKind(i) if i >= 0: result = DirKind(i)
else: result = dkNone else: result = dkNone
proc parseLine(p: var TRstParser, father: PRstNode) = proc parseLine(p: var RstParser, father: PRstNode) =
while true: while true:
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father) of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father)
else: break else: break
proc parseUntilNewline(p: var TRstParser, father: PRstNode) = proc parseUntilNewline(p: var RstParser, father: PRstNode) =
while true: while true:
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father) of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
of tkEof, tkIndent: break of tkEof, tkIndent: break
proc parseSection(p: var TRstParser, result: PRstNode) {.gcsafe.} proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.}
proc parseField(p: var TRstParser): PRstNode = proc parseField(p: var RstParser): PRstNode =
## Returns a parsed rnField node. ## Returns a parsed rnField node.
## ##
## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody. ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
@ -897,7 +905,7 @@ proc parseField(p: var TRstParser): PRstNode =
add(result, fieldname) add(result, fieldname)
add(result, fieldbody) add(result, fieldbody)
proc parseFields(p: var TRstParser): PRstNode = proc parseFields(p: var RstParser): PRstNode =
## Parses fields for a section or directive block. ## Parses fields for a section or directive block.
## ##
## This proc may return nil if the parsing doesn't find anything of value, ## This proc may return nil if the parsing doesn't find anything of value,
@ -947,8 +955,8 @@ proc getArgument(n: PRstNode): string =
if n.sons[0] == nil: result = "" if n.sons[0] == nil: result = ""
else: result = addNodes(n.sons[0]) else: result = addNodes(n.sons[0])
proc parseDotDot(p: var TRstParser): PRstNode {.gcsafe.} proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.}
proc parseLiteralBlock(p: var TRstParser): PRstNode = proc parseLiteralBlock(p: var RstParser): PRstNode =
result = newRstNode(rnLiteralBlock) result = newRstNode(rnLiteralBlock)
var n = newRstNode(rnLeaf, "") var n = newRstNode(rnLeaf, "")
if p.tok[p.idx].kind == tkIndent: if p.tok[p.idx].kind == tkIndent:
@ -974,13 +982,13 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
inc(p.idx) inc(p.idx)
add(result, n) add(result, n)
proc getLevel(map: var TLevelMap, lvl: var int, c: char): int = proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
if map[c] == 0: if map[c] == 0:
inc(lvl) inc(lvl)
map[c] = lvl map[c] = lvl
result = map[c] result = map[c]
proc tokenAfterNewline(p: TRstParser): int = proc tokenAfterNewline(p: RstParser): int =
result = p.idx result = p.idx
while true: while true:
case p.tok[result].kind case p.tok[result].kind
@ -991,28 +999,28 @@ proc tokenAfterNewline(p: TRstParser): int =
break break
else: inc(result) else: inc(result)
proc isLineBlock(p: TRstParser): bool = proc isLineBlock(p: RstParser): bool =
var j = tokenAfterNewline(p) var j = tokenAfterNewline(p)
result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or
(p.tok[j].col > p.tok[p.idx].col) (p.tok[j].col > p.tok[p.idx].col)
proc predNL(p: TRstParser): bool = proc predNL(p: RstParser): bool =
result = true result = true
if p.idx > 0: if p.idx > 0:
result = p.tok[p.idx-1].kind == tkIndent and result = p.tok[p.idx-1].kind == tkIndent and
p.tok[p.idx-1].ival == currInd(p) p.tok[p.idx-1].ival == currInd(p)
proc isDefList(p: TRstParser): bool = proc isDefList(p: RstParser): bool =
var j = tokenAfterNewline(p) var j = tokenAfterNewline(p)
result = (p.tok[p.idx].col < p.tok[j].col) and result = (p.tok[p.idx].col < p.tok[j].col) and
(p.tok[j].kind in {tkWord, tkOther, tkPunct}) and (p.tok[j].kind in {tkWord, tkOther, tkPunct}) and
(p.tok[j - 2].symbol != "::") (p.tok[j - 2].symbol != "::")
proc isOptionList(p: TRstParser): bool = proc isOptionList(p: RstParser): bool =
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
match(p, p.idx, "/w") or match(p, p.idx, "//w") match(p, p.idx, "/w") or match(p, p.idx, "//w")
proc whichSection(p: TRstParser): TRstNodeKind = proc whichSection(p: RstParser): RstNodeKind =
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkAdornment: of tkAdornment:
if match(p, p.idx + 1, "ii"): result = rnTransition if match(p, p.idx + 1, "ii"): result = rnTransition
@ -1053,7 +1061,7 @@ proc whichSection(p: TRstParser): TRstNodeKind =
else: result = rnParagraph else: result = rnParagraph
else: result = rnLeaf else: result = rnLeaf
proc parseLineBlock(p: var TRstParser): PRstNode = proc parseLineBlock(p: var RstParser): PRstNode =
result = nil result = nil
if p.tok[p.idx + 1].kind == tkWhite: if p.tok[p.idx + 1].kind == tkWhite:
var col = p.tok[p.idx].col var col = p.tok[p.idx].col
@ -1072,7 +1080,7 @@ proc parseLineBlock(p: var TRstParser): PRstNode =
break break
popInd(p) popInd(p)
proc parseParagraph(p: var TRstParser, result: PRstNode) = proc parseParagraph(p: var RstParser, result: PRstNode) =
while true: while true:
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkIndent: of tkIndent:
@ -1103,7 +1111,7 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) =
parseInline(p, result) parseInline(p, result)
else: break else: break
proc parseHeadline(p: var TRstParser): PRstNode = proc parseHeadline(p: var RstParser): PRstNode =
result = newRstNode(rnHeadline) result = newRstNode(rnHeadline)
parseUntilNewline(p, result) parseUntilNewline(p, result)
assert(p.tok[p.idx].kind == tkIndent) assert(p.tok[p.idx].kind == tkIndent)
@ -1113,12 +1121,13 @@ proc parseHeadline(p: var TRstParser): PRstNode =
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c) result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
type type
TIntSeq = seq[int] IntSeq = seq[int]
{.deprecated: [TIntSeq: IntSeq].}
proc tokEnd(p: TRstParser): int = proc tokEnd(p: RstParser): int =
result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1 result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
proc getColumns(p: var TRstParser, cols: var TIntSeq) = proc getColumns(p: var RstParser, cols: var IntSeq) =
var L = 0 var L = 0
while true: while true:
inc(L) inc(L)
@ -1133,15 +1142,15 @@ proc getColumns(p: var TRstParser, cols: var TIntSeq) =
# last column has no limit: # last column has no limit:
cols[L - 1] = 32000 cols[L - 1] = 32000
proc parseDoc(p: var TRstParser): PRstNode {.gcsafe.} proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
proc parseSimpleTable(p: var TRstParser): PRstNode = proc parseSimpleTable(p: var RstParser): PRstNode =
var var
cols: TIntSeq cols: IntSeq
row: seq[string] row: seq[string]
i, last, line: int i, last, line: int
c: char c: char
q: TRstParser q: RstParser
a, b: PRstNode a, b: PRstNode
result = newRstNode(rnTable) result = newRstNode(rnTable)
cols = @[] cols = @[]
@ -1188,13 +1197,13 @@ proc parseSimpleTable(p: var TRstParser): PRstNode =
add(a, b) add(a, b)
add(result, a) add(result, a)
proc parseTransition(p: var TRstParser): PRstNode = proc parseTransition(p: var RstParser): PRstNode =
result = newRstNode(rnTransition) result = newRstNode(rnTransition)
inc(p.idx) inc(p.idx)
if p.tok[p.idx].kind == tkIndent: inc(p.idx) if p.tok[p.idx].kind == tkIndent: inc(p.idx)
if p.tok[p.idx].kind == tkIndent: inc(p.idx) if p.tok[p.idx].kind == tkIndent: inc(p.idx)
proc parseOverline(p: var TRstParser): PRstNode = proc parseOverline(p: var RstParser): PRstNode =
var c = p.tok[p.idx].symbol[0] var c = p.tok[p.idx].symbol[0]
inc(p.idx, 2) inc(p.idx, 2)
result = newRstNode(rnOverline) result = newRstNode(rnOverline)
@ -1213,7 +1222,7 @@ proc parseOverline(p: var TRstParser): PRstNode =
inc(p.idx) # XXX: check? inc(p.idx) # XXX: check?
if p.tok[p.idx].kind == tkIndent: inc(p.idx) if p.tok[p.idx].kind == tkIndent: inc(p.idx)
proc parseBulletList(p: var TRstParser): PRstNode = proc parseBulletList(p: var RstParser): PRstNode =
result = nil result = nil
if p.tok[p.idx + 1].kind == tkWhite: if p.tok[p.idx + 1].kind == tkWhite:
var bullet = p.tok[p.idx].symbol var bullet = p.tok[p.idx].symbol
@ -1233,7 +1242,7 @@ proc parseBulletList(p: var TRstParser): PRstNode =
break break
popInd(p) popInd(p)
proc parseOptionList(p: var TRstParser): PRstNode = proc parseOptionList(p: var RstParser): PRstNode =
result = newRstNode(rnOptionList) result = newRstNode(rnOptionList)
while true: while true:
if isOptionList(p): if isOptionList(p):
@ -1262,7 +1271,7 @@ proc parseOptionList(p: var TRstParser): PRstNode =
else: else:
break break
proc parseDefinitionList(p: var TRstParser): PRstNode = proc parseDefinitionList(p: var RstParser): PRstNode =
result = nil result = nil
var j = tokenAfterNewline(p) - 1 var j = tokenAfterNewline(p) - 1
if (j >= 1) and (p.tok[j].kind == tkIndent) and if (j >= 1) and (p.tok[j].kind == tkIndent) and
@ -1298,7 +1307,7 @@ proc parseDefinitionList(p: var TRstParser): PRstNode =
break break
if len(result) == 0: result = nil if len(result) == 0: result = nil
proc parseEnumList(p: var TRstParser): PRstNode = proc parseEnumList(p: var RstParser): PRstNode =
const const
wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "] wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "]
wildpos: array[0..2, int] = [1, 0, 0] wildpos: array[0..2, int] = [1, 0, 0]
@ -1328,11 +1337,11 @@ proc parseEnumList(p: var TRstParser): PRstNode =
dec(p.idx, wildpos[w] + 3) dec(p.idx, wildpos[w] + 3)
result = nil result = nil
proc sonKind(father: PRstNode, i: int): TRstNodeKind = proc sonKind(father: PRstNode, i: int): RstNodeKind =
result = rnLeaf result = rnLeaf
if i < len(father): result = father.sons[i].kind if i < len(father): result = father.sons[i].kind
proc parseSection(p: var TRstParser, result: PRstNode) = proc parseSection(p: var RstParser, result: PRstNode) =
while true: while true:
var leave = false var leave = false
assert(p.idx >= 0) assert(p.idx >= 0)
@ -1380,16 +1389,16 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph:
result.sons[0].kind = rnInner result.sons[0].kind = rnInner
proc parseSectionWrapper(p: var TRstParser): PRstNode = proc parseSectionWrapper(p: var RstParser): PRstNode =
result = newRstNode(rnInner) result = newRstNode(rnInner)
parseSection(p, result) parseSection(p, result)
while (result.kind == rnInner) and (len(result) == 1): while (result.kind == rnInner) and (len(result) == 1):
result = result.sons[0] result = result.sons[0]
proc `$`(t: TToken): string = proc `$`(t: Token): string =
result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol) result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol)
proc parseDoc(p: var TRstParser): PRstNode = proc parseDoc(p: var RstParser): PRstNode =
result = parseSectionWrapper(p) result = parseSectionWrapper(p)
if p.tok[p.idx].kind != tkEof: if p.tok[p.idx].kind != tkEof:
when false: when false:
@ -1403,12 +1412,14 @@ proc parseDoc(p: var TRstParser): PRstNode =
rstMessage(p, meGeneralParseError) rstMessage(p, meGeneralParseError)
type type
TDirFlag = enum DirFlag = enum
hasArg, hasOptions, argIsFile, argIsWord hasArg, hasOptions, argIsFile, argIsWord
TDirFlags = set[TDirFlag] DirFlags = set[DirFlag]
TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.} SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags,
TSectionParser: SectionParser].}
proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
## Parses arguments and options for a directive block. ## Parses arguments and options for a directive block.
## ##
## A directive block will always have three sons: the arguments for the ## A directive block will always have three sons: the arguments for the
@ -1446,11 +1457,11 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
options = parseFields(p) options = parseFields(p)
add(result, options) add(result, options)
proc indFollows(p: TRstParser): bool = proc indFollows(p: RstParser): bool =
result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p) result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
proc parseDirective(p: var TRstParser, flags: TDirFlags, proc parseDirective(p: var RstParser, flags: DirFlags,
contentParser: TSectionParser): PRstNode = contentParser: SectionParser): PRstNode =
## Returns a generic rnDirective tree. ## Returns a generic rnDirective tree.
## ##
## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil. ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
@ -1463,13 +1474,13 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags,
else: else:
add(result, nil) add(result, nil)
proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode = proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode =
if indFollows(p): if indFollows(p):
pushInd(p, p.tok[p.idx].ival) pushInd(p, p.tok[p.idx].ival)
result = contentParser(p) result = contentParser(p)
popInd(p) popInd(p)
proc dirInclude(p: var TRstParser): PRstNode = proc dirInclude(p: var RstParser): PRstNode =
# #
#The following options are recognized: #The following options are recognized:
# #
@ -1498,7 +1509,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
result = newRstNode(rnLiteralBlock) result = newRstNode(rnLiteralBlock)
add(result, newRstNode(rnLeaf, readFile(path))) add(result, newRstNode(rnLeaf, readFile(path)))
else: else:
var q: TRstParser var q: RstParser
initParser(q, p.s) initParser(q, p.s)
q.filename = filename q.filename = filename
q.col += getTokens(readFile(path), false, q.tok) q.col += getTokens(readFile(path), false, q.tok)
@ -1507,7 +1518,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
# InternalError("Too many binary zeros in include file") # InternalError("Too many binary zeros in include file")
result = parseDoc(q) result = parseDoc(q)
proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode = proc dirCodeBlock(p: var RstParser, nimrodExtension = false): PRstNode =
## Parses a code block. ## Parses a code block.
## ##
## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the ## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the
@ -1548,35 +1559,35 @@ proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
result.kind = rnCodeBlock result.kind = rnCodeBlock
proc dirContainer(p: var TRstParser): PRstNode = proc dirContainer(p: var RstParser): PRstNode =
result = parseDirective(p, {hasArg}, parseSectionWrapper) result = parseDirective(p, {hasArg}, parseSectionWrapper)
assert(result.kind == rnDirective) assert(result.kind == rnDirective)
assert(len(result) == 3) assert(len(result) == 3)
result.kind = rnContainer result.kind = rnContainer
proc dirImage(p: var TRstParser): PRstNode = proc dirImage(p: var RstParser): PRstNode =
result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil) result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil)
result.kind = rnImage result.kind = rnImage
proc dirFigure(p: var TRstParser): PRstNode = proc dirFigure(p: var RstParser): PRstNode =
result = parseDirective(p, {hasOptions, hasArg, argIsFile}, result = parseDirective(p, {hasOptions, hasArg, argIsFile},
parseSectionWrapper) parseSectionWrapper)
result.kind = rnFigure result.kind = rnFigure
proc dirTitle(p: var TRstParser): PRstNode = proc dirTitle(p: var RstParser): PRstNode =
result = parseDirective(p, {hasArg}, nil) result = parseDirective(p, {hasArg}, nil)
result.kind = rnTitle result.kind = rnTitle
proc dirContents(p: var TRstParser): PRstNode = proc dirContents(p: var RstParser): PRstNode =
result = parseDirective(p, {hasArg}, nil) result = parseDirective(p, {hasArg}, nil)
result.kind = rnContents result.kind = rnContents
proc dirIndex(p: var TRstParser): PRstNode = proc dirIndex(p: var RstParser): PRstNode =
result = parseDirective(p, {}, parseSectionWrapper) result = parseDirective(p, {}, parseSectionWrapper)
result.kind = rnIndex result.kind = rnIndex
proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind, proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
contentParser: TSectionParser) = contentParser: SectionParser) =
var filename = getFieldValue(result, "file") var filename = getFieldValue(result, "file")
if filename.len > 0: if filename.len > 0:
var path = p.s.findFile(filename) var path = p.s.findFile(filename)
@ -1590,7 +1601,7 @@ proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
result.kind = kind result.kind = kind
add(result, parseDirBody(p, contentParser)) add(result, parseDirBody(p, contentParser))
proc dirRaw(p: var TRstParser): PRstNode = proc dirRaw(p: var RstParser): PRstNode =
# #
#The following options are recognized: #The following options are recognized:
# #
@ -1610,7 +1621,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
else: else:
dirRawAux(p, result, rnRaw, parseSectionWrapper) dirRawAux(p, result, rnRaw, parseSectionWrapper)
proc parseDotDot(p: var TRstParser): PRstNode = proc parseDotDot(p: var RstParser): PRstNode =
result = nil result = nil
var col = p.tok[p.idx].col var col = p.tok[p.idx].col
inc(p.idx) inc(p.idx)
@ -1667,7 +1678,7 @@ proc parseDotDot(p: var TRstParser): PRstNode =
else: else:
result = parseComment(p) result = parseComment(p)
proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode = proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
result = n result = n
if n == nil: return if n == nil: return
case n.kind case n.kind
@ -1696,10 +1707,10 @@ proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
proc rstParse*(text, filename: string, proc rstParse*(text, filename: string,
line, column: int, hasToc: var bool, line, column: int, hasToc: var bool,
options: TRstParseOptions, options: RstParseOptions,
findFile: TFindFileHandler = nil, findFile: FindFileHandler = nil,
msgHandler: TMsgHandler = nil): PRstNode = msgHandler: MsgHandler = nil): PRstNode =
var p: TRstParser var p: RstParser
initParser(p, newSharedState(options, findFile, msgHandler)) initParser(p, newSharedState(options, findFile, msgHandler))
p.filename = filename p.filename = filename
p.line = line p.line = line

View file

@ -12,7 +12,7 @@
import strutils, json import strutils, json
type type
TRstNodeKind* = enum ## the possible node kinds of an PRstNode RstNodeKind* = enum ## the possible node kinds of an PRstNode
rnInner, # an inner node or a root rnInner, # an inner node or a root
rnHeadline, # a headline rnHeadline, # a headline
rnOverline, # an over- and underlined headline rnOverline, # an over- and underlined headline
@ -62,24 +62,26 @@ type
# leaf val # leaf val
PRstNode* = ref TRstNode ## an RST node PRstNode* = ref RstNode ## an RST node
TRstNodeSeq* = seq[PRstNode] RstNodeSeq* = seq[PRstNode]
TRstNode* {.acyclic, final.} = object ## an RST node's description RstNode* {.acyclic, final.} = object ## an RST node's description
kind*: TRstNodeKind ## the node's kind kind*: RstNodeKind ## the node's kind
text*: string ## valid for leafs in the AST; and the title of text*: string ## valid for leafs in the AST; and the title of
## the document or the section ## the document or the section
level*: int ## valid for some node kinds level*: int ## valid for some node kinds
sons*: TRstNodeSeq ## the node's sons sons*: RstNodeSeq ## the node's sons
{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq,
TRstNode: RstNode].}
proc len*(n: PRstNode): int = proc len*(n: PRstNode): int =
result = len(n.sons) result = len(n.sons)
proc newRstNode*(kind: TRstNodeKind): PRstNode = proc newRstNode*(kind: RstNodeKind): PRstNode =
new(result) new(result)
result.sons = @[] result.sons = @[]
result.kind = kind result.kind = kind
proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode = proc newRstNode*(kind: RstNodeKind, s: string): PRstNode =
result = newRstNode(kind) result = newRstNode(kind)
result.text = s result.text = s
@ -94,18 +96,19 @@ proc addIfNotNil*(father, son: PRstNode) =
type type
TRenderContext {.pure.} = object RenderContext {.pure.} = object
indent: int indent: int
verbatim: int verbatim: int
{.deprecated: [TRenderContext: RenderContext].}
proc renderRstToRst(d: var TRenderContext, n: PRstNode, proc renderRstToRst(d: var RenderContext, n: PRstNode,
result: var string) {.gcsafe.} result: var string) {.gcsafe.}
proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) = proc renderRstSons(d: var RenderContext, n: PRstNode, result: var string) =
for i in countup(0, len(n) - 1): for i in countup(0, len(n) - 1):
renderRstToRst(d, n.sons[i], result) renderRstToRst(d, n.sons[i], result)
proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
# this is needed for the index generation; it may also be useful for # this is needed for the index generation; it may also be useful for
# debugging, but most code is already debugged... # debugging, but most code is already debugged...
const const
@ -284,7 +287,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
proc renderRstToRst*(n: PRstNode, result: var string) = proc renderRstToRst*(n: PRstNode, result: var string) =
## renders `n` into its string representation and appends to `result`. ## renders `n` into its string representation and appends to `result`.
var d: TRenderContext var d: RenderContext
renderRstToRst(d, n, result) renderRstToRst(d, n, result)
proc renderRstToJsonNode(node: PRstNode): JsonNode = proc renderRstToJsonNode(node: PRstNode): JsonNode =

View file

@ -18,7 +18,7 @@
## document you provide yourself, so it won't contain the usual ``<header>`` or ## document you provide yourself, so it won't contain the usual ``<header>`` or
## ``<body>`` parts. ## ``<body>`` parts.
## ##
## You can also create a ``TRstGenerator`` structure and populate it with the ## You can also create a ``RstGenerator`` structure and populate it with the
## other lower level methods to finally build complete documents. This requires ## other lower level methods to finally build complete documents. This requires
## many options and tweaking, but you are not limited to snippets and can ## many options and tweaking, but you are not limited to snippets and can
## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too. ## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
@ -31,29 +31,29 @@ const
IndexExt* = ".idx" IndexExt* = ".idx"
type type
TOutputTarget* = enum ## which document type to generate OutputTarget* = enum ## which document type to generate
outHtml, # output is HTML outHtml, # output is HTML
outLatex # output is Latex outLatex # output is Latex
TTocEntry = object TocEntry = object
n*: PRstNode n*: PRstNode
refname*, header*: string refname*, header*: string
TMetaEnum* = enum MetaEnum* = enum
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
TRstGenerator* = object of RootObj RstGenerator* = object of RootObj
target*: TOutputTarget target*: OutputTarget
config*: StringTableRef config*: StringTableRef
splitAfter*: int # split too long entries in the TOC splitAfter*: int # split too long entries in the TOC
tocPart*: seq[TTocEntry] tocPart*: seq[TocEntry]
hasToc*: bool hasToc*: bool
theIndex: string # Contents of the index file to be dumped at the end. theIndex: string # Contents of the index file to be dumped at the end.
options*: TRstParseOptions options*: RstParseOptions
findFile*: TFindFileHandler findFile*: FindFileHandler
msgHandler*: TMsgHandler msgHandler*: MsgHandler
filename*: string filename*: string
meta*: array[TMetaEnum, string] meta*: array[MetaEnum, string]
currentSection: string ## \ currentSection: string ## \
## Stores the empty string or the last headline/overline found in the rst ## Stores the empty string or the last headline/overline found in the rst
## document, so it can be used as a prettier name for term index generation. ## document, so it can be used as a prettier name for term index generation.
@ -61,14 +61,15 @@ type
## Keeps count of same text index terms to generate different identifiers ## Keeps count of same text index terms to generate different identifiers
## for hyperlinks. See renderIndexTerm proc for details. ## for hyperlinks. See renderIndexTerm proc for details.
PDoc = var TRstGenerator ## Alias to type less. PDoc = var RstGenerator ## Alias to type less.
CodeBlockParams = object ## Stores code block params. CodeBlockParams = object ## Stores code block params.
numberLines: bool ## True if the renderer has to show line numbers. numberLines: bool ## True if the renderer has to show line numbers.
startLine: int ## The starting line of the code block, by default 1. startLine: int ## The starting line of the code block, by default 1.
langStr: string ## Input string used to specify the language. langStr: string ## Input string used to specify the language.
lang: TSourceLanguage ## Type of highlighting, by default none. lang: SourceLanguage ## Type of highlighting, by default none.
{.deprecated: [TRstGenerator: RstGenerator, TTocEntry: TocEntry,
TOutputTarget: OutputTarget, TMetaEnum: MetaEnum].}
proc init(p: var CodeBlockParams) = proc init(p: var CodeBlockParams) =
## Default initialisation of CodeBlockParams to sane values. ## Default initialisation of CodeBlockParams to sane values.
@ -76,14 +77,14 @@ proc init(p: var CodeBlockParams) =
p.lang = langNone p.lang = langNone
p.langStr = "" p.langStr = ""
proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
config: StringTableRef, filename: string, config: StringTableRef, filename: string,
options: TRstParseOptions, options: RstParseOptions,
findFile: TFindFileHandler=nil, findFile: FindFileHandler=nil,
msgHandler: TMsgHandler=nil) = msgHandler: MsgHandler=nil) =
## Initializes a ``TRstGenerator``. ## Initializes a ``RstGenerator``.
## ##
## You need to call this before using a ``TRstGenerator`` with any other ## You need to call this before using a ``RstGenerator`` with any other
## procs in this module. Pass a non ``nil`` ``StringTableRef`` value as ## procs in this module. Pass a non ``nil`` ``StringTableRef`` value as
## `config` with parameters used by the HTML output generator. If you don't ## `config` with parameters used by the HTML output generator. If you don't
## know what to use, pass the results of the `defaultConfig() ## know what to use, pass the results of the `defaultConfig()
@ -96,7 +97,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
## filename``. This default title can be overriden by the embedded rst, but ## filename``. This default title can be overriden by the embedded rst, but
## it helps to prettify the generated index if no title is found. ## it helps to prettify the generated index if no title is found.
## ##
## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types ## The ``RstParseOptions``, ``FindFileHandler`` and ``MsgHandler`` types
## are defined in the the `packages/docutils/rst module <rst.html>`_. ## are defined in the the `packages/docutils/rst module <rst.html>`_.
## ``options`` selects the behaviour of the rst parser. ## ``options`` selects the behaviour of the rst parser.
## ##
@ -120,7 +121,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
## ##
## import packages/docutils/rstgen ## import packages/docutils/rstgen
## ##
## var gen: TRstGenerator ## var gen: RstGenerator
## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {}) ## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {})
g.config = config g.config = config
g.target = target g.target = target
@ -141,7 +142,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
if s != "": g.splitAfter = parseInt(s) if s != "": g.splitAfter = parseInt(s)
for i in low(g.meta)..high(g.meta): g.meta[i] = "" for i in low(g.meta)..high(g.meta): g.meta[i] = ""
proc writeIndexFile*(g: var TRstGenerator, outfile: string) = proc writeIndexFile*(g: var RstGenerator, outfile: string) =
## Writes the current index buffer to the specified output file. ## Writes the current index buffer to the specified output file.
## ##
## You previously need to add entries to the index with the `setIndexTerm() ## You previously need to add entries to the index with the `setIndexTerm()
@ -183,7 +184,7 @@ proc addTexChar(dest: var string, c: char) =
var splitter*: string = "<wbr />" var splitter*: string = "<wbr />"
proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} = proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
case target case target
of outHtml: addXmlChar(dest, c) of outHtml: addXmlChar(dest, c)
of outLatex: addTexChar(dest, c) of outLatex: addTexChar(dest, c)
@ -200,7 +201,7 @@ proc nextSplitPoint*(s: string, start: int): int =
inc(result) inc(result)
dec(result) # last valid index dec(result) # last valid index
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string = proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
result = "" result = ""
if splitAfter >= 0: if splitAfter >= 0:
var partLen = 0 var partLen = 0
@ -217,16 +218,16 @@ proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
for i in countup(0, len(s) - 1): escChar(target, result, s[i]) for i in countup(0, len(s) - 1): escChar(target, result, s[i])
proc disp(target: TOutputTarget, xml, tex: string): string = proc disp(target: OutputTarget, xml, tex: string): string =
if target != outLatex: result = xml if target != outLatex: result = xml
else: result = tex else: result = tex
proc dispF(target: TOutputTarget, xml, tex: string, proc dispF(target: OutputTarget, xml, tex: string,
args: varargs[string]): string = args: varargs[string]): string =
if target != outLatex: result = xml % args if target != outLatex: result = xml % args
else: result = tex % args else: result = tex % args
proc dispA(target: TOutputTarget, dest: var string, proc dispA(target: OutputTarget, dest: var string,
xml, tex: string, args: varargs[string]) = xml, tex: string, args: varargs[string]) =
if target != outLatex: addf(dest, xml, args) if target != outLatex: addf(dest, xml, args)
else: addf(dest, tex, args) else: addf(dest, tex, args)
@ -234,10 +235,10 @@ proc dispA(target: TOutputTarget, dest: var string,
proc `or`(x, y: string): string {.inline.} = proc `or`(x, y: string): string {.inline.} =
result = if x.isNil: y else: x result = if x.isNil: y else: x
proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string) proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration. ## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
## ##
## Before using this proc you need to initialise a ``TRstGenerator`` with ## Before using this proc you need to initialise a ``RstGenerator`` with
## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the ## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
## `packages/docutils/rst module <rst.html>`_. Example: ## `packages/docutils/rst module <rst.html>`_. Example:
## ##
@ -277,7 +278,7 @@ proc unquoteIndexColumn(text: string): string =
## Returns the unquoted version generated by ``quoteIndexColumn``. ## Returns the unquoted version generated by ``quoteIndexColumn``.
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\") result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
proc setIndexTerm*(d: var TRstGenerator, id, term: string, proc setIndexTerm*(d: var RstGenerator, id, term: string,
linkTitle, linkDesc = "") = linkTitle, linkDesc = "") =
## Adds a `term` to the index using the specified hyperlink identifier. ## Adds a `term` to the index using the specified hyperlink identifier.
## ##
@ -351,30 +352,30 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
[id, term]) [id, term])
type type
TIndexEntry = object IndexEntry = object
keyword: string keyword: string
link: string link: string
linkTitle: string ## If not nil, contains a prettier text for the href linkTitle: string ## If not nil, contains a prettier text for the href
linkDesc: string ## If not nil, the title attribute of the final href linkDesc: string ## If not nil, the title attribute of the final href
TIndexedDocs = Table[TIndexEntry, seq[TIndexEntry]] ## \ IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
## Contains the index sequences for doc types. ## Contains the index sequences for doc types.
## ##
## The key is a *fake* TIndexEntry which will contain the title of the ## The key is a *fake* IndexEntry which will contain the title of the
## document in the `keyword` field and `link` will contain the html ## document in the `keyword` field and `link` will contain the html
## filename for the document. `linkTitle` and `linkDesc` will be nil. ## filename for the document. `linkTitle` and `linkDesc` will be nil.
## ##
## The value indexed by this TIndexEntry is a sequence with the real index ## The value indexed by this IndexEntry is a sequence with the real index
## entries found in the ``.idx`` file. ## entries found in the ``.idx`` file.
{.deprecated: [TIndexEntry: IndexEntry, TIndexedDocs: IndexedDocs].}
proc cmp(a, b: IndexEntry): int =
proc cmp(a, b: TIndexEntry): int = ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`.
result = cmpIgnoreStyle(a.keyword, b.keyword) result = cmpIgnoreStyle(a.keyword, b.keyword)
if result == 0: if result == 0:
result = cmpIgnoreStyle(a.link, b.link) result = cmpIgnoreStyle(a.link, b.link)
proc hash(x: TIndexEntry): THash = proc hash(x: IndexEntry): Hash =
## Returns the hash for the combined fields of the type. ## Returns the hash for the combined fields of the type.
## ##
## The hash is computed as the chained hash of the individual string hashes. ## The hash is computed as the chained hash of the individual string hashes.
@ -385,7 +386,7 @@ proc hash(x: TIndexEntry): THash =
result = result !& (x.linkDesc or "").hash result = result !& (x.linkDesc or "").hash
result = !$result result = !$result
proc `<-`(a: var TIndexEntry, b: TIndexEntry) = proc `<-`(a: var IndexEntry, b: IndexEntry) =
shallowCopy a.keyword, b.keyword shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link shallowCopy a.link, b.link
if b.linkTitle.isNil: a.linkTitle = nil if b.linkTitle.isNil: a.linkTitle = nil
@ -393,7 +394,7 @@ proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
if b.linkDesc.isNil: a.linkDesc = nil if b.linkDesc.isNil: a.linkDesc = nil
else: shallowCopy a.linkDesc, b.linkDesc else: shallowCopy a.linkDesc, b.linkDesc
proc sortIndex(a: var openArray[TIndexEntry]) = proc sortIndex(a: var openArray[IndexEntry]) =
# we use shellsort here; fast and simple # we use shellsort here; fast and simple
let n = len(a) let n = len(a)
var h = 1 var h = 1
@ -403,7 +404,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
while true: while true:
h = h div 3 h = h div 3
for i in countup(h, n - 1): for i in countup(h, n - 1):
var v: TIndexEntry var v: IndexEntry
v <- a[i] v <- a[i]
var j = i var j = i
while cmp(a[j-h], v) >= 0: while cmp(a[j-h], v) >= 0:
@ -413,7 +414,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
a[j] <- v a[j] <- v
if h == 1: break if h == 1: break
proc generateSymbolIndex(symbols: seq[TIndexEntry]): string = proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
result = "" result = ""
var i = 0 var i = 0
while i < symbols.len: while i < symbols.len:
@ -466,7 +467,7 @@ proc indentToLevel(level: var int, newLevel: int): string =
result = repeat("</ul>", level - newLevel) result = repeat("</ul>", level - newLevel)
level = newLevel level = newLevel
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string = proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
## Returns the sequence of index entries in an HTML hierarchical list. ## Returns the sequence of index entries in an HTML hierarchical list.
result = "" result = ""
# Build a list of levels and extracted titles to make processing easier. # Build a list of levels and extracted titles to make processing easier.
@ -507,12 +508,12 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
assert(not titleRef.isNil, assert(not titleRef.isNil,
"Can't use this proc on an API index, docs always have a title entry") "Can't use this proc on an API index, docs always have a title entry")
proc generateDocumentationIndex(docs: TIndexedDocs): string = proc generateDocumentationIndex(docs: IndexedDocs): string =
## Returns all the documentation TOCs in an HTML hierarchical list. ## Returns all the documentation TOCs in an HTML hierarchical list.
result = "" result = ""
# Sort the titles to generate their toc in alphabetical order. # Sort the titles to generate their toc in alphabetical order.
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs)) var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
sort(titles, cmp) sort(titles, cmp)
for title in titles: for title in titles:
@ -520,12 +521,12 @@ proc generateDocumentationIndex(docs: TIndexedDocs): string =
result.add("<ul><li><a href=\"" & result.add("<ul><li><a href=\"" &
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n") title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n")
proc generateDocumentationJumps(docs: TIndexedDocs): string = proc generateDocumentationJumps(docs: IndexedDocs): string =
## Returns a plain list of hyperlinks to documentation TOCs in HTML. ## Returns a plain list of hyperlinks to documentation TOCs in HTML.
result = "Documents: " result = "Documents: "
# Sort the titles to generate their toc in alphabetical order. # Sort the titles to generate their toc in alphabetical order.
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs)) var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
sort(titles, cmp) sort(titles, cmp)
var chunks: seq[string] = @[] var chunks: seq[string] = @[]
@ -545,14 +546,14 @@ proc generateModuleJumps(modules: seq[string]): string =
result.add(chunks.join(", ") & ".<br>") result.add(chunks.join(", ") & ".<br>")
proc readIndexDir(dir: string): proc readIndexDir(dir: string):
tuple[modules: seq[string], symbols: seq[TIndexEntry], docs: TIndexedDocs] = tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items. ## Walks `dir` reading ``.idx`` files converting them in IndexEntry items.
## ##
## Returns the list of found module names, the list of free symbol entries ## Returns the list of found module names, the list of free symbol entries
## and the different documentation indexes. The list of modules is sorted. ## and the different documentation indexes. The list of modules is sorted.
## See the documentation of ``mergeIndexes`` for details. ## See the documentation of ``mergeIndexes`` for details.
result.modules = @[] result.modules = @[]
result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32) result.docs = initTable[IndexEntry, seq[IndexEntry]](32)
newSeq(result.symbols, 15_000) newSeq(result.symbols, 15_000)
setLen(result.symbols, 0) setLen(result.symbols, 0)
var L = 0 var L = 0
@ -560,8 +561,8 @@ proc readIndexDir(dir: string):
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
if kind == pcFile and path.endsWith(IndexExt): if kind == pcFile and path.endsWith(IndexExt):
var var
fileEntries: seq[TIndexEntry] fileEntries: seq[IndexEntry]
title: TIndexEntry title: IndexEntry
F = 0 F = 0
newSeq(fileEntries, 500) newSeq(fileEntries, 500)
setLen(fileEntries, 0) setLen(fileEntries, 0)
@ -662,7 +663,7 @@ proc mergeIndexes*(dir: string): string =
proc stripTOCHTML(s: string): string = proc stripTOCHTML(s: string): string =
## Ugly quick hack to remove HTML tags from TOC titles. ## Ugly quick hack to remove HTML tags from TOC titles.
## ##
## A TTocEntry.header field already contains rendered HTML tags. Instead of ## A TocEntry.header field already contains rendered HTML tags. Instead of
## implementing a proper version of renderRstToOut() which recursively ## implementing a proper version of renderRstToOut() which recursively
## renders an rst tree to plain text, we simply remove text found between ## renders an rst tree to plain text, we simply remove text found between
## angled brackets. Given the limited possibilities of rst inside TOC titles ## angled brackets. Given the limited possibilities of rst inside TOC titles
@ -728,12 +729,12 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))]) rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) = proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
dispA(d.target, result, dispA(d.target, result,
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n", "<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header]) "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
result: var string) = result: var string) =
var tmp = "" var tmp = ""
while j <= high(d.tocPart): while j <= high(d.tocPart):
@ -878,7 +879,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
for letter in m.text: escChar(d.target, result, letter) for letter in m.text: escChar(d.target, result, letter)
else: else:
var g: TGeneralTokenizer var g: GeneralTokenizer
initGeneralTokenizer(g, m.text) initGeneralTokenizer(g, m.text)
while true: while true:
getNextToken(g, params.lang) getNextToken(g, params.lang)
@ -1214,7 +1215,7 @@ $content
# ---------- forum --------------------------------------------------------- # ---------- forum ---------------------------------------------------------
proc rstToHtml*(s: string, options: TRstParseOptions, proc rstToHtml*(s: string, options: RstParseOptions,
config: StringTableRef): string = config: StringTableRef): string =
## Converts an input rst string into embeddable HTML. ## Converts an input rst string into embeddable HTML.
## ##
@ -1233,7 +1234,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
## # --> <em>Hello</em> <strong>world</strong>! ## # --> <em>Hello</em> <strong>world</strong>!
## ##
## If you need to allow the rst ``include`` directive or tweak the generated ## If you need to allow the rst ``include`` directive or tweak the generated
## output you have to create your own ``TRstGenerator`` with ## output you have to create your own ``RstGenerator`` with
## ``initRstGenerator`` and related procs. ## ``initRstGenerator`` and related procs.
proc myFindFile(filename: string): string = proc myFindFile(filename: string): string =
@ -1241,7 +1242,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
result = "" result = ""
const filen = "input" const filen = "input"
var d: TRstGenerator var d: RstGenerator
initRstGenerator(d, outHtml, config, filen, options, myFindFile, initRstGenerator(d, outHtml, config, filen, options, myFindFile,
rst.defaultMsgHandler) rst.defaultMsgHandler)
var dummyHasToc = false var dummyHasToc = false

View file

@ -12,14 +12,15 @@
# Get the platform-dependent flags. # Get the platform-dependent flags.
# Structure describing an inotify event. # Structure describing an inotify event.
type type
Tinotify_event*{.pure, final, importc: "struct inotify_event", InotifyEvent*{.pure, final, importc: "struct inotify_event",
header: "<sys/inotify.h>".} = object header: "<sys/inotify.h>".} = object
wd*{.importc: "wd".}: cint # Watch descriptor. wd*{.importc: "wd".}: cint # Watch descriptor.
mask*{.importc: "mask".}: uint32 # Watch mask. mask*{.importc: "mask".}: uint32 # Watch mask.
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events. cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
len*{.importc: "len".}: uint32 # Length (including NULs) of name. len*{.importc: "len".}: uint32 # Length (including NULs) of name.
name*{.importc: "name".}: char # Name. name*{.importc: "name".}: char # Name.
{.deprecated: [Tinotify_event: InotifyEvent].}
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
const const
IN_ACCESS* = 0x00000001 # File was accessed. IN_ACCESS* = 0x00000001 # File was accessed.
@ -69,4 +70,4 @@ proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".} cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
# Remove the watch specified by WD from the inotify instance FD. # Remove the watch specified by WD from the inotify instance FD.
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl, proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
importc: "inotify_rm_watch", header: "<sys/inotify.h>".} importc: "inotify_rm_watch", header: "<sys/inotify.h>".}

View file

@ -24,5 +24,5 @@ const
# fn should be of type proc (a2: pointer): void {.cdecl.} # fn should be of type proc (a2: pointer): void {.cdecl.}
proc clone*(fn: pointer; child_stack: pointer; flags: cint; proc clone*(fn: pointer; child_stack: pointer; flags: cint;
arg: pointer; ptid: ptr TPid; tls: pointer; arg: pointer; ptid: ptr Pid; tls: pointer;
ctid: ptr TPid): cint {.importc, header: "<sched.h>".} ctid: ptr Pid): cint {.importc, header: "<sched.h>".}

File diff suppressed because it is too large Load diff

View file

@ -12,17 +12,18 @@ import posix
type type
Speed* = cuint Speed* = cuint
Tcflag* = cuint Cflag* = cuint
{.deprecated: [Tcflag: Cflag].}
const const
NCCS* = 32 NCCS* = 32
type type
Termios* {.importc: "struct termios", header: "<termios.h>".} = object Termios* {.importc: "struct termios", header: "<termios.h>".} = object
c_iflag*: Tcflag # input mode flags c_iflag*: Cflag # input mode flags
c_oflag*: Tcflag # output mode flags c_oflag*: Cflag # output mode flags
c_cflag*: Tcflag # control mode flags c_cflag*: Cflag # control mode flags
c_lflag*: Tcflag # local mode flags c_lflag*: Cflag # local mode flags
c_line*: cuchar # line discipline c_line*: cuchar # line discipline
c_cc*: array[NCCS, cuchar] # control characters c_cc*: array[NCCS, cuchar] # control characters
@ -258,4 +259,4 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
header: "<termios.h>".} header: "<termios.h>".}
# Get process group ID for session leader for controlling terminal FD. # Get process group ID for session leader for controlling terminal FD.
proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".} proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".}

View file

@ -16,7 +16,7 @@
## .. code-block:: nim ## .. code-block:: nim
## ##
## var ## var
## a: TActorPool[int, void] ## a: ActorPool[int, void]
## createActorPool(a) ## createActorPool(a)
## for i in 0 .. < 300: ## for i in 0 .. < 300:
## a.spawn(i, proc (x: int) {.thread.} = echo x) ## a.spawn(i, proc (x: int) {.thread.} = echo x)
@ -30,75 +30,76 @@
from os import sleep from os import sleep
type type
TTask*[TIn, TOut] = object{.pure, final.} ## a task Task*[In, Out] = object{.pure, final.} ## a task
when TOut isnot void: when Out isnot void:
receiver*: ptr TChannel[TOut] ## the receiver channel of the response receiver*: ptr Channel[Out] ## the receiver channel of the response
action*: proc (x: TIn): TOut {.thread.} ## action to execute; action*: proc (x: In): Out {.thread.} ## action to execute;
## sometimes useful ## sometimes useful
shutDown*: bool ## set to tell an actor to shut-down shutDown*: bool ## set to tell an actor to shut-down
data*: TIn ## the data to process data*: In ## the data to process
TActor[TIn, TOut] = object{.pure, final.} Actor[In, Out] = object{.pure, final.}
i: TChannel[TTask[TIn, TOut]] i: Channel[Task[In, Out]]
t: TThread[ptr TActor[TIn, TOut]] t: TThread[ptr Actor[In, Out]]
PActor*[TIn, TOut] = ptr TActor[TIn, TOut] ## an actor PActor*[In, Out] = ptr Actor[In, Out] ## an actor
{.deprecated: [TTask: Task, TActor: Actor].}
proc spawn*[TIn, TOut](action: proc(
self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] = proc spawn*[In, Out](action: proc(
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
## creates an actor; that is a thread with an inbox. The caller MUST call ## creates an actor; that is a thread with an inbox. The caller MUST call
## ``join`` because that also frees the actor's associated resources. ## ``join`` because that also frees the actor's associated resources.
result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[]))) result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
open(result.i) open(result.i)
createThread(result.t, action, result) createThread(result.t, action, result)
proc inbox*[TIn, TOut](self: PActor[TIn, TOut]): ptr TChannel[TIn] = proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
## gets a pointer to the associated inbox of the actor `self`. ## gets a pointer to the associated inbox of the actor `self`.
result = addr(self.i) result = addr(self.i)
proc running*[TIn, TOut](a: PActor[TIn, TOut]): bool = proc running*[In, Out](a: PActor[In, Out]): bool =
## returns true if the actor `a` is running. ## returns true if the actor `a` is running.
result = running(a.t) result = running(a.t)
proc ready*[TIn, TOut](a: PActor[TIn, TOut]): bool = proc ready*[In, Out](a: PActor[In, Out]): bool =
## returns true if the actor `a` is ready to process new messages. ## returns true if the actor `a` is ready to process new messages.
result = ready(a.i) result = ready(a.i)
proc join*[TIn, TOut](a: PActor[TIn, TOut]) = proc join*[In, Out](a: PActor[In, Out]) =
## joins an actor. ## joins an actor.
joinThread(a.t) joinThread(a.t)
close(a.i) close(a.i)
deallocShared(a) deallocShared(a)
proc recv*[TIn, TOut](a: PActor[TIn, TOut]): TTask[TIn, TOut] = proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
## receives a task from `a`'s inbox. ## receives a task from `a`'s inbox.
result = recv(a.i) result = recv(a.i)
proc send*[TIn, TOut, X, Y](receiver: PActor[TIn, TOut], msg: TIn, proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
sender: PActor[X, Y]) = sender: PActor[X, Y]) =
## sends a message to `a`'s inbox. ## sends a message to `a`'s inbox.
var t: TTask[TIn, TOut] var t: Task[In, Out]
t.receiver = addr(sender.i) t.receiver = addr(sender.i)
shallowCopy(t.data, msg) shallowCopy(t.data, msg)
send(receiver.i, t) send(receiver.i, t)
proc send*[TIn, TOut](receiver: PActor[TIn, TOut], msg: TIn, proc send*[In, Out](receiver: PActor[In, Out], msg: In,
sender: ptr TChannel[TOut] = nil) = sender: ptr Channel[Out] = nil) =
## sends a message to `receiver`'s inbox. ## sends a message to `receiver`'s inbox.
var t: TTask[TIn, TOut] var t: Task[In, Out]
t.receiver = sender t.receiver = sender
shallowCopy(t.data, msg) shallowCopy(t.data, msg)
send(receiver.i, t) send(receiver.i, t)
proc sendShutdown*[TIn, TOut](receiver: PActor[TIn, TOut]) = proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
## send a shutdown message to `receiver`. ## send a shutdown message to `receiver`.
var t: TTask[TIn, TOut] var t: Task[In, Out]
t.shutdown = true t.shutdown = true
send(receiver.i, t) send(receiver.i, t)
proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) = proc reply*[In, Out](t: Task[In, Out], m: Out) =
## sends a message to io's output message box. ## sends a message to io's output message box.
when TOut is void: when Out is void:
{.error: "you cannot reply to a void outbox".} {.error: "you cannot reply to a void outbox".}
assert t.receiver != nil assert t.receiver != nil
send(t.receiver[], m) send(t.receiver[], m)
@ -107,34 +108,35 @@ proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
# ----------------- actor pools ---------------------------------------------- # ----------------- actor pools ----------------------------------------------
type type
TActorPool*[TIn, TOut] = object{.pure, final.} ## an actor pool ActorPool*[In, Out] = object{.pure, final.} ## an actor pool
actors: seq[PActor[TIn, TOut]] actors: seq[PActor[In, Out]]
when TOut isnot void: when Out isnot void:
outputs: TChannel[TOut] outputs: Channel[Out]
{.deprecated: [TActorPool: ActorPool].}
proc `^`*[T](f: ptr TChannel[T]): T = proc `^`*[T](f: ptr Channel[T]): T =
## alias for 'recv'. ## alias for 'recv'.
result = recv(f[]) result = recv(f[])
proc poolWorker[TIn, TOut](self: PActor[TIn, TOut]) {.thread.} = proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
while true: while true:
var m = self.recv var m = self.recv
if m.shutDown: break if m.shutDown: break
when TOut is void: when Out is void:
m.action(m.data) m.action(m.data)
else: else:
send(m.receiver[], m.action(m.data)) send(m.receiver[], m.action(m.data))
#self.reply() #self.reply()
proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) = proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
## creates an actor pool. ## creates an actor pool.
newSeq(a.actors, poolSize) newSeq(a.actors, poolSize)
when TOut isnot void: when Out isnot void:
open(a.outputs) open(a.outputs)
for i in 0 .. < a.actors.len: for i in 0 .. < a.actors.len:
a.actors[i] = spawn(poolWorker[TIn, TOut]) a.actors[i] = spawn(poolWorker[In, Out])
proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) = proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
## waits for every actor of `a` to finish with its work. Currently this is ## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms and has a slight chance ## implemented as polling every `polling` ms and has a slight chance
## of failing since we check for every actor to be in `ready` state and not ## of failing since we check for every actor to be in `ready` state and not
@ -157,18 +159,18 @@ proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
if allReadyCount > 1: break if allReadyCount > 1: break
sleep(polling) sleep(polling)
proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) = proc terminate*[In, Out](a: var ActorPool[In, Out]) =
## terminates each actor in the actor pool `a` and frees the ## terminates each actor in the actor pool `a` and frees the
## resources attached to `a`. ## resources attached to `a`.
var t: TTask[TIn, TOut] var t: Task[In, Out]
t.shutdown = true t.shutdown = true
for i in 0.. <a.actors.len: send(a.actors[i].i, t) for i in 0.. <a.actors.len: send(a.actors[i].i, t)
for i in 0.. <a.actors.len: join(a.actors[i]) for i in 0.. <a.actors.len: join(a.actors[i])
when TOut isnot void: when Out isnot void:
close(a.outputs) close(a.outputs)
a.actors = nil a.actors = nil
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) = proc join*[In, Out](a: var ActorPool[In, Out]) =
## short-cut for `sync` and then `terminate`. ## short-cut for `sync` and then `terminate`.
sync(a) sync(a)
terminate(a) terminate(a)
@ -202,28 +204,28 @@ template schedule =
else: else:
raise newException(DeadThreadError, "cannot send message; thread died") raise newException(DeadThreadError, "cannot send message; thread died")
proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn, proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
action: proc (input: TIn): TOut {.thread.} action: proc (input: In): Out {.thread.}
): ptr TChannel[TOut] = ): ptr Channel[Out] =
## uses the actor pool to run ``action(input)`` concurrently. ## uses the actor pool to run ``action(input)`` concurrently.
## `spawn` is guaranteed to not block. ## `spawn` is guaranteed to not block.
var t: TTask[TIn, TOut] var t: Task[In, Out]
setupTask() setupTask()
result = addr(p.outputs) result = addr(p.outputs)
t.receiver = result t.receiver = result
schedule() schedule()
proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn, proc spawn*[In](p: var ActorPool[In, void], input: In,
action: proc (input: TIn) {.thread.}) = action: proc (input: In) {.thread.}) =
## uses the actor pool to run ``action(input)`` concurrently. ## uses the actor pool to run ``action(input)`` concurrently.
## `spawn` is guaranteed to not block. ## `spawn` is guaranteed to not block.
var t: TTask[TIn, void] var t: Task[In, void]
setupTask() setupTask()
schedule() schedule()
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
var var
a: TActorPool[int, void] a: ActorPool[int, void]
createActorPool(a) createActorPool(a)
for i in 0 .. < 300: for i in 0 .. < 300:
a.spawn(i, proc (x: int) {.thread.} = echo x) a.spawn(i, proc (x: int) {.thread.} = echo x)

View file

@ -323,32 +323,34 @@ proc processTimers(p: PDispatcherBase) =
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
import winlean, sets, hashes import winlean, sets, hashes
type type
TCompletionKey = Dword CompletionKey = Dword
TCompletionData* = object CompletionData* = object
fd*: TAsyncFD # TODO: Rename this. fd*: AsyncFD # TODO: Rename this.
cb*: proc (fd: TAsyncFD, bytesTransferred: Dword, cb*: proc (fd: AsyncFD, bytesTransferred: Dword,
errcode: OSErrorCode) {.closure,gcsafe.} errcode: OSErrorCode) {.closure,gcsafe.}
PDispatcher* = ref object of PDispatcherBase PDispatcher* = ref object of PDispatcherBase
ioPort: THandle ioPort: Handle
handles: HashSet[TAsyncFD] handles: HashSet[AsyncFD]
TCustomOverlapped = object of TOVERLAPPED CustomOverlapped = object of TOVERLAPPED
data*: TCompletionData data*: CompletionData
PCustomOverlapped* = ref TCustomOverlapped PCustomOverlapped* = ref CustomOverlapped
TAsyncFD* = distinct int AsyncFD* = distinct int
{.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
proc hash(x: TAsyncFD): THash {.borrow.} proc hash(x: AsyncFD): Hash {.borrow.}
proc `==`*(x: TAsyncFD, y: TAsyncFD): bool {.borrow.} proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.}
proc newDispatcher*(): PDispatcher = proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance. ## Creates a new Dispatcher instance.
new result new result
result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
result.handles = initSet[TAsyncFD]() result.handles = initSet[AsyncFD]()
result.timers = @[] result.timers = @[]
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
@ -357,15 +359,15 @@ when defined(windows) or defined(nimdoc):
if gDisp.isNil: gDisp = newDispatcher() if gDisp.isNil: gDisp = newDispatcher()
result = gDisp result = gDisp
proc register*(fd: TAsyncFD) = proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher. ## Registers ``fd`` with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if createIoCompletionPort(fd.THandle, p.ioPort, if createIoCompletionPort(fd.Handle, p.ioPort,
cast[TCompletionKey](fd), 1) == 0: cast[CompletionKey](fd), 1) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
p.handles.incl(fd) p.handles.incl(fd)
proc verifyPresence(fd: TAsyncFD) = proc verifyPresence(fd: AsyncFD) =
## Ensures that file descriptor has been registered with the dispatcher. ## Ensures that file descriptor has been registered with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if fd notin p.handles: if fd notin p.handles:
@ -394,7 +396,7 @@ when defined(windows) or defined(nimdoc):
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
if res: if res:
# This is useful for ensuring the reliability of the overlapped struct. # This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd, customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, OSErrorCode(-1)) lpNumberOfBytesTransferred, OSErrorCode(-1))
@ -402,7 +404,7 @@ when defined(windows) or defined(nimdoc):
else: else:
let errCode = osLastError() let errCode = osLastError()
if customOverlapped != nil: if customOverlapped != nil:
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd, customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, errCode) lpNumberOfBytesTransferred, errCode)
GC_unref(customOverlapped) GC_unref(customOverlapped)
@ -480,7 +482,7 @@ when defined(windows) or defined(nimdoc):
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength) RemoteSockaddr, RemoteSockaddrLength)
proc connect*(socket: TAsyncFD, address: string, port: Port, proc connect*(socket: AsyncFD, address: string, port: Port,
af = AF_INET): Future[void] = af = AF_INET): Future[void] =
## Connects ``socket`` to server at ``address:port``. ## Connects ``socket`` to server at ``address:port``.
## ##
@ -506,8 +508,8 @@ when defined(windows) or defined(nimdoc):
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb: ol.data = CompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
retFuture.complete() retFuture.complete()
@ -542,7 +544,7 @@ when defined(windows) or defined(nimdoc):
retFuture.fail(newException(OSError, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
return retFuture return retFuture
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: AsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): Future[string] = flags = {SocketFlag.SafeDisconn}): Future[string] =
## Reads **up to** ``size`` bytes from ``socket``. Returned future will ## Reads **up to** ``size`` bytes from ``socket``. Returned future will
## complete once all the data requested is read, a part of the data has been ## complete once all the data requested is read, a part of the data has been
@ -570,8 +572,8 @@ when defined(windows) or defined(nimdoc):
var flagsio = flags.toOSFlags().Dword var flagsio = flags.toOSFlags().Dword
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb: ol.data = CompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0': if bytesCount == 0 and dataBuf.buf[0] == '\0':
@ -634,7 +636,7 @@ when defined(windows) or defined(nimdoc):
# free ``ol``. # free ``ol``.
return retFuture return retFuture
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int, proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] = flags = {SocketFlag.SafeDisconn}): Future[int] =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
## at least be of that size. Returned future will complete once all the ## at least be of that size. Returned future will complete once all the
@ -665,8 +667,8 @@ when defined(windows) or defined(nimdoc):
var flagsio = flags.toOSFlags().Dword var flagsio = flags.toOSFlags().Dword
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb: ol.data = CompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0': if bytesCount == 0 and dataBuf.buf[0] == '\0':
@ -721,7 +723,7 @@ when defined(windows) or defined(nimdoc):
# free ``ol``. # free ``ol``.
return retFuture return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent. ## data has been sent.
@ -735,8 +737,8 @@ when defined(windows) or defined(nimdoc):
var bytesReceived, lowFlags: Dword var bytesReceived, lowFlags: Dword
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb: ol.data = CompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
retFuture.complete() retFuture.complete()
@ -764,8 +766,8 @@ when defined(windows) or defined(nimdoc):
# free ``ol``. # free ``ol``.
return retFuture return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}): proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: TAsyncFD]] = Future[tuple[address: string, client: AsyncFD]] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client. ## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
@ -778,7 +780,7 @@ when defined(windows) or defined(nimdoc):
## flag is specified then this error will not be raised and instead ## flag is specified then this error will not be raised and instead
## accept will be called again. ## accept will be called again.
verifyPresence(socket) verifyPresence(socket)
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr") var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
var clientSock = newRawSocket() var clientSock = newRawSocket()
if clientSock == osInvalidSocket: raiseOSError(osLastError()) if clientSock == osInvalidSocket: raiseOSError(osLastError())
@ -803,11 +805,11 @@ when defined(windows) or defined(nimdoc):
dwLocalAddressLength, dwRemoteAddressLength, dwLocalAddressLength, dwRemoteAddressLength,
addr localSockaddr, addr localLen, addr localSockaddr, addr localLen,
addr remoteSockaddr, addr remoteLen) addr remoteSockaddr, addr remoteLen)
register(clientSock.TAsyncFD) register(clientSock.AsyncFD)
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186 # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
retFuture.complete( retFuture.complete(
(address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr), (address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
client: clientSock.TAsyncFD) client: clientSock.AsyncFD)
) )
template failAccept(errcode): stmt = template failAccept(errcode): stmt =
@ -824,8 +826,8 @@ when defined(windows) or defined(nimdoc):
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb: ol.data = CompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
completeAccept() completeAccept()
@ -853,26 +855,26 @@ when defined(windows) or defined(nimdoc):
return retFuture return retFuture
proc newAsyncRawSocket*(domain, typ, protocol: cint): TAsyncFD = proc newAsyncRawSocket*(domain, typ, protocol: cint): AsyncFD =
## Creates a new socket and registers it with the dispatcher implicitly. ## Creates a new socket and registers it with the dispatcher implicitly.
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
proc newAsyncRawSocket*(domain: Domain = AF_INET, proc newAsyncRawSocket*(domain: Domain = AF_INET,
typ: SockType = SOCK_STREAM, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): TAsyncFD = protocol: Protocol = IPPROTO_TCP): AsyncFD =
## Creates a new socket and registers it with the dispatcher implicitly. ## Creates a new socket and registers it with the dispatcher implicitly.
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
proc closeSocket*(socket: TAsyncFD) = proc closeSocket*(socket: AsyncFD) =
## Closes a socket and ensures that it is unregistered. ## Closes a socket and ensures that it is unregistered.
socket.SocketHandle.close() socket.SocketHandle.close()
getGlobalDispatcher().handles.excl(socket) getGlobalDispatcher().handles.excl(socket)
proc unregister*(fd: TAsyncFD) = proc unregister*(fd: AsyncFD) =
## Unregisters ``fd``. ## Unregisters ``fd``.
getGlobalDispatcher().handles.excl(fd) getGlobalDispatcher().handles.excl(fd)
@ -892,18 +894,19 @@ else:
MSG_NOSIGNAL MSG_NOSIGNAL
type type
TAsyncFD* = distinct cint AsyncFD* = distinct cint
TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.} Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
PData* = ref object of RootRef PData* = ref object of RootRef
fd: TAsyncFD fd: AsyncFD
readCBs: seq[TCallback] readCBs: seq[Callback]
writeCBs: seq[TCallback] writeCBs: seq[Callback]
PDispatcher* = ref object of PDispatcherBase PDispatcher* = ref object of PDispatcherBase
selector: Selector selector: Selector
{.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].}
proc `==`*(x, y: TAsyncFD): bool {.borrow.} proc `==`*(x, y: AsyncFD): bool {.borrow.}
proc newDispatcher*(): PDispatcher = proc newDispatcher*(): PDispatcher =
new result new result
@ -915,18 +918,18 @@ else:
if gDisp.isNil: gDisp = newDispatcher() if gDisp.isNil: gDisp = newDispatcher()
result = gDisp result = gDisp
proc update(fd: TAsyncFD, events: set[Event]) = proc update(fd: AsyncFD, events: set[Event]) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
assert fd.SocketHandle in p.selector assert fd.SocketHandle in p.selector
discard p.selector.update(fd.SocketHandle, events) discard p.selector.update(fd.SocketHandle, events)
proc register*(fd: TAsyncFD) = proc register*(fd: AsyncFD) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = PData(fd: fd, readCBs: @[], writeCBs: @[]) var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
p.selector.register(fd.SocketHandle, {}, data.RootRef) p.selector.register(fd.SocketHandle, {}, data.RootRef)
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD = proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): AsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
when defined(macosx): when defined(macosx):
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
@ -934,29 +937,29 @@ else:
proc newAsyncRawSocket*(domain: Domain = AF_INET, proc newAsyncRawSocket*(domain: Domain = AF_INET,
typ: SockType = SOCK_STREAM, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): TAsyncFD = protocol: Protocol = IPPROTO_TCP): AsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
when defined(macosx): when defined(macosx):
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
register(result) register(result)
proc closeSocket*(sock: TAsyncFD) = proc closeSocket*(sock: AsyncFD) =
let disp = getGlobalDispatcher() let disp = getGlobalDispatcher()
sock.SocketHandle.close() sock.SocketHandle.close()
disp.selector.unregister(sock.SocketHandle) disp.selector.unregister(sock.SocketHandle)
proc unregister*(fd: TAsyncFD) = proc unregister*(fd: AsyncFD) =
getGlobalDispatcher().selector.unregister(fd.SocketHandle) getGlobalDispatcher().selector.unregister(fd.SocketHandle)
proc addRead*(fd: TAsyncFD, cb: TCallback) = proc addRead*(fd: AsyncFD, cb: Callback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector: if fd.SocketHandle notin p.selector:
raise newException(ValueError, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
p.selector[fd.SocketHandle].data.PData.readCBs.add(cb) p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvRead}) update(fd, p.selector[fd.SocketHandle].events + {EvRead})
proc addWrite*(fd: TAsyncFD, cb: TCallback) = proc addWrite*(fd: AsyncFD, cb: Callback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector: if fd.SocketHandle notin p.selector:
raise newException(ValueError, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
@ -967,7 +970,7 @@ else:
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
for info in p.selector.select(timeout): for info in p.selector.select(timeout):
let data = PData(info.key.data) let data = PData(info.key.data)
assert data.fd == info.key.fd.TAsyncFD assert data.fd == info.key.fd.AsyncFD
#echo("In poll ", data.fd.cint) #echo("In poll ", data.fd.cint)
if EvError in info.events: if EvError in info.events:
closeSocket(data.fd) closeSocket(data.fd)
@ -1005,11 +1008,11 @@ else:
processTimers(p) processTimers(p)
proc connect*(socket: TAsyncFD, address: string, port: Port, proc connect*(socket: AsyncFD, address: string, port: Port,
af = AF_INET): Future[void] = af = AF_INET): Future[void] =
var retFuture = newFuture[void]("connect") var retFuture = newFuture[void]("connect")
proc cb(fd: TAsyncFD): bool = proc cb(fd: AsyncFD): bool =
# We have connected. # We have connected.
retFuture.complete() retFuture.complete()
return true return true
@ -1040,13 +1043,13 @@ else:
retFuture.fail(newException(OSError, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
return retFuture return retFuture
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: AsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): Future[string] = flags = {SocketFlag.SafeDisconn}): Future[string] =
var retFuture = newFuture[string]("recv") var retFuture = newFuture[string]("recv")
var readBuffer = newString(size) var readBuffer = newString(size)
proc cb(sock: TAsyncFD): bool = proc cb(sock: AsyncFD): bool =
result = true result = true
let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint, let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint,
flags.toOSFlags()) flags.toOSFlags())
@ -1070,11 +1073,11 @@ else:
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int, proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] = flags = {SocketFlag.SafeDisconn}): Future[int] =
var retFuture = newFuture[int]("recvInto") var retFuture = newFuture[int]("recvInto")
proc cb(sock: TAsyncFD): bool = proc cb(sock: AsyncFD): bool =
result = true result = true
let res = recv(sock.SocketHandle, buf, size.cint, let res = recv(sock.SocketHandle, buf, size.cint,
flags.toOSFlags()) flags.toOSFlags())
@ -1094,13 +1097,13 @@ else:
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
var written = 0 var written = 0
proc cb(sock: TAsyncFD): bool = proc cb(sock: AsyncFD): bool =
result = true result = true
let netSize = data.len-written let netSize = data.len-written
var d = data.cstring var d = data.cstring
@ -1126,11 +1129,11 @@ else:
addWrite(socket, cb) addWrite(socket, cb)
return retFuture return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}): proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: TAsyncFD]] = Future[tuple[address: string, client: AsyncFD]] =
var retFuture = newFuture[tuple[address: string, var retFuture = newFuture[tuple[address: string,
client: TAsyncFD]]("acceptAddr") client: AsyncFD]]("acceptAddr")
proc cb(sock: TAsyncFD): bool = proc cb(sock: AsyncFD): bool =
result = true result = true
var sockAddress: SockAddr_in var sockAddress: SockAddr_in
var addrLen = sizeof(sockAddress).Socklen var addrLen = sizeof(sockAddress).Socklen
@ -1147,8 +1150,8 @@ else:
else: else:
retFuture.fail(newException(OSError, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else: else:
register(client.TAsyncFD) register(client.AsyncFD)
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD)) retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.AsyncFD))
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
@ -1160,15 +1163,15 @@ proc sleepAsync*(ms: int): Future[void] =
p.timers.add((epochTime() + (ms / 1000), retFuture)) p.timers.add((epochTime() + (ms / 1000), retFuture))
return retFuture return retFuture
proc accept*(socket: TAsyncFD, proc accept*(socket: AsyncFD,
flags = {SocketFlag.SafeDisconn}): Future[TAsyncFD] = flags = {SocketFlag.SafeDisconn}): Future[AsyncFD] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection. ## corresponding to that connection.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TAsyncFD]("accept") var retFut = newFuture[AsyncFD]("accept")
var fut = acceptAddr(socket, flags) var fut = acceptAddr(socket, flags)
fut.callback = fut.callback =
proc (future: Future[tuple[address: string, client: TAsyncFD]]) = proc (future: Future[tuple[address: string, client: AsyncFD]]) =
assert future.finished assert future.finished
if future.failed: if future.failed:
retFut.fail(future.error) retFut.fail(future.error)
@ -1495,7 +1498,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
#if prc[0].getName == "test": #if prc[0].getName == "test":
# echo(toStrLit(result)) # echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs. ## a full line is read or an error occurs.
## ##

View file

@ -31,7 +31,7 @@ else:
type type
AsyncFile* = ref object AsyncFile* = ref object
fd: TAsyncFd fd: AsyncFd
offset: int64 offset: int64
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
@ -72,7 +72,7 @@ proc getFileSize(f: AsyncFile): int64 =
## Retrieves the specified file's size. ## Retrieves the specified file's size.
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
var high: DWord var high: DWord
let low = getFileSize(f.fd.THandle, addr high) let low = getFileSize(f.fd.Handle, addr high)
if low == INVALID_FILE_SIZE: if low == INVALID_FILE_SIZE:
raiseOSError(osLastError()) raiseOSError(osLastError())
return (high shl 32) or low return (high shl 32) or low
@ -88,13 +88,13 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
when useWinUnicode: when useWinUnicode:
result.fd = createFileW(newWideCString(filename), desiredAccess, result.fd = createFileW(newWideCString(filename), desiredAccess,
FILE_SHARE_READ, FILE_SHARE_READ,
nil, creationDisposition, flags, 0).TAsyncFd nil, creationDisposition, flags, 0).AsyncFd
else: else:
result.fd = createFileA(filename, desiredAccess, result.fd = createFileA(filename, desiredAccess,
FILE_SHARE_READ, FILE_SHARE_READ,
nil, creationDisposition, flags, 0).TAsyncFd nil, creationDisposition, flags, 0).AsyncFd
if result.fd.THandle == INVALID_HANDLE_VALUE: if result.fd.Handle == INVALID_HANDLE_VALUE:
raiseOSError(osLastError()) raiseOSError(osLastError())
register(result.fd) register(result.fd)
@ -106,7 +106,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
let flags = getPosixFlags(mode) let flags = getPosixFlags(mode)
# RW (Owner), RW (Group), R (Other) # RW (Owner), RW (Group), R (Other)
let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH
result.fd = open(filename, flags, perm).TAsyncFD result.fd = open(filename, flags, perm).AsyncFD
if result.fd.cint == -1: if result.fd.cint == -1:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -125,8 +125,8 @@ proc read*(f: AsyncFile, size: int): Future[string] =
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: f.fd, cb: ol.data = CompletionData(fd: f.fd, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
assert bytesCount > 0 assert bytesCount > 0
@ -148,7 +148,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
ol.offsetHigh = DWord(f.offset shr 32) ol.offsetHigh = DWord(f.offset shr 32)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesRead. # According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
let ret = readFile(f.fd.THandle, buffer, size.int32, nil, let ret = readFile(f.fd.Handle, buffer, size.int32, nil,
cast[POVERLAPPED](ol)) cast[POVERLAPPED](ol))
if not ret.bool: if not ret.bool:
let err = osLastError() let err = osLastError()
@ -161,7 +161,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
else: else:
# Request completed immediately. # Request completed immediately.
var bytesRead: DWord var bytesRead: DWord
let overlappedRes = getOverlappedResult(f.fd.THandle, let overlappedRes = getOverlappedResult(f.fd.Handle,
cast[POverlapped](ol)[], bytesRead, false.WinBool) cast[POverlapped](ol)[], bytesRead, false.WinBool)
if not overlappedRes.bool: if not overlappedRes.bool:
let err = osLastError() let err = osLastError()
@ -179,7 +179,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
else: else:
var readBuffer = newString(size) var readBuffer = newString(size)
proc cb(fd: TAsyncFD): bool = proc cb(fd: AsyncFD): bool =
result = true result = true
let res = read(fd.cint, addr readBuffer[0], size.cint) let res = read(fd.cint, addr readBuffer[0], size.cint)
if res < 0: if res < 0:
@ -251,8 +251,8 @@ proc write*(f: AsyncFile, data: string): Future[void] =
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(fd: f.fd, cb: ol.data = CompletionData(fd: f.fd, cb:
proc (fd: TAsyncFD, bytesCount: DWord, errcode: OSErrorCode) = proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
assert bytesCount == data.len.int32 assert bytesCount == data.len.int32
@ -268,7 +268,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
ol.offsetHigh = DWord(f.offset shr 32) ol.offsetHigh = DWord(f.offset shr 32)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten. # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.THandle, buffer, data.len.int32, nil, let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
cast[POVERLAPPED](ol)) cast[POVERLAPPED](ol))
if not ret.bool: if not ret.bool:
let err = osLastError() let err = osLastError()
@ -281,7 +281,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
else: else:
# Request completed immediately. # Request completed immediately.
var bytesWritten: DWord var bytesWritten: DWord
let overlappedRes = getOverlappedResult(f.fd.THandle, let overlappedRes = getOverlappedResult(f.fd.Handle,
cast[POverlapped](ol)[], bytesWritten, false.WinBool) cast[POverlapped](ol)[], bytesWritten, false.WinBool)
if not overlappedRes.bool: if not overlappedRes.bool:
retFuture.fail(newException(OSError, osErrorMsg(osLastError()))) retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
@ -292,7 +292,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
else: else:
var written = 0 var written = 0
proc cb(fd: TAsyncFD): bool = proc cb(fd: AsyncFD): bool =
result = true result = true
let remainderSize = data.len-written let remainderSize = data.len-written
let res = write(fd.cint, addr copy[written], remainderSize.cint) let res = write(fd.cint, addr copy[written], remainderSize.cint)
@ -317,7 +317,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
proc close*(f: AsyncFile) = proc close*(f: AsyncFile) =
## Closes the file specified. ## Closes the file specified.
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
if not closeHandle(f.fd.THandle).bool: if not closeHandle(f.fd.Handle).bool:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:
if close(f.fd.cint) == -1: if close(f.fd.cint) == -1:

View file

@ -210,6 +210,7 @@ proc processClient(client: AsyncSocket, address: string,
var contentLength = 0 var contentLength = 0
if parseInt(request.headers["Content-Length"], contentLength) == 0: if parseInt(request.headers["Content-Length"], contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.") await request.respond(Http400, "Bad Request. Invalid Content-Length.")
continue
else: else:
request.body = await client.recv(contentLength) request.body = await client.recv(contentLength)
assert request.body.len == contentLength assert request.body.len == contentLength

View file

@ -188,8 +188,8 @@ proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
result.socket.setBlocking(false) result.socket.setBlocking(false)
proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket = proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
## Wraps an already initialized ``TSocket`` into a AsyncSocket. ## Wraps an already initialized ``Socket`` into a AsyncSocket.
## This is useful if you want to use an already connected TSocket as an ## This is useful if you want to use an already connected Socket as an
## asynchronous AsyncSocket in asyncio's event loop. ## asynchronous AsyncSocket in asyncio's event loop.
## ##
## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be ## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be

View file

@ -91,9 +91,9 @@ type
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect. # TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
proc newAsyncSocket*(fd: TAsyncFD, isBuff: bool): AsyncSocket = proc newAsyncSocket*(fd: AsyncFD, isBuff: bool): AsyncSocket =
## Creates a new ``AsyncSocket`` based on the supplied params. ## Creates a new ``AsyncSocket`` based on the supplied params.
assert fd != osInvalidSocket.TAsyncFD assert fd != osInvalidSocket.AsyncFD
new(result) new(result)
result.fd = fd.SocketHandle result.fd = fd.SocketHandle
result.isBuffered = isBuff result.isBuffered = isBuff
@ -142,7 +142,7 @@ when defined(ssl):
if read < 0: if read < 0:
raiseSslError() raiseSslError()
data.setLen(read) data.setLen(read)
await socket.fd.TAsyncFd.send(data, flags) await socket.fd.AsyncFd.send(data, flags)
proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
sslError: cint) {.async.} = sslError: cint) {.async.} =
@ -150,7 +150,7 @@ when defined(ssl):
of SSL_ERROR_WANT_WRITE: of SSL_ERROR_WANT_WRITE:
await sendPendingSslData(socket, flags) await sendPendingSslData(socket, flags)
of SSL_ERROR_WANT_READ: of SSL_ERROR_WANT_READ:
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags) var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint) let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
if ret < 0: if ret < 0:
raiseSSLError() raiseSSLError()
@ -175,7 +175,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
## ##
## Returns a ``Future`` which will complete when the connection succeeds ## Returns a ``Future`` which will complete when the connection succeeds
## or an error occurs. ## or an error occurs.
await connect(socket.fd.TAsyncFD, address, port, af) await connect(socket.fd.AsyncFD, address, port, af)
if socket.isSsl: if socket.isSsl:
when defined(ssl): when defined(ssl):
let flags = {SocketFlag.SafeDisconn} let flags = {SocketFlag.SafeDisconn}
@ -194,7 +194,7 @@ template readInto(buf: cstring, size: int, socket: AsyncSocket,
sslRead(socket.sslHandle, buf, size.cint)) sslRead(socket.sslHandle, buf, size.cint))
res = opResult res = opResult
else: else:
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags) var recvIntoFut = recvInto(socket.fd.AsyncFD, buf, size, flags)
yield recvIntoFut yield recvIntoFut
# Not in SSL mode. # Not in SSL mode.
res = recvIntoFut.read() res = recvIntoFut.read()
@ -271,7 +271,7 @@ proc send*(socket: AsyncSocket, data: string,
sslWrite(socket.sslHandle, addr copy[0], copy.len.cint)) sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
await sendPendingSslData(socket, flags) await sendPendingSslData(socket, flags)
else: else:
await send(socket.fd.TAsyncFD, data, flags) await send(socket.fd.AsyncFD, data, flags)
proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: AsyncSocket]] = Future[tuple[address: string, client: AsyncSocket]] =
@ -279,9 +279,9 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
## corresponding to that connection and the remote address of the client. ## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr") var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
var fut = acceptAddr(socket.fd.TAsyncFD, flags) var fut = acceptAddr(socket.fd.AsyncFD, flags)
fut.callback = fut.callback =
proc (future: Future[tuple[address: string, client: TAsyncFD]]) = proc (future: Future[tuple[address: string, client: AsyncFD]]) =
assert future.finished assert future.finished
if future.failed: if future.failed:
retFuture.fail(future.readError) retFuture.fail(future.readError)
@ -445,7 +445,7 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
proc close*(socket: AsyncSocket) = proc close*(socket: AsyncSocket) =
## Closes the socket. ## Closes the socket.
defer: defer:
socket.fd.TAsyncFD.closeSocket() socket.fd.AsyncFD.closeSocket()
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
let res = SslShutdown(socket.sslHandle) let res = SslShutdown(socket.sslHandle)

View file

@ -20,20 +20,20 @@ import strutils
## ##
## # Create a matrix which first rotates, then scales and at last translates ## # Create a matrix which first rotates, then scales and at last translates
## ##
## var m:TMatrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0) ## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
## ##
## # Create a 2d point at (100,0) and a vector (5,2) ## # Create a 2d point at (100,0) and a vector (5,2)
## ##
## var pt:TPoint2d=point2d(100.0,0.0) ## var pt:Point2d=point2d(100.0,0.0)
## ##
## var vec:TVector2d=vector2d(5.0,2.0) ## var vec:Vector2d=vector2d(5.0,2.0)
## ##
## ##
## pt &= m # transforms pt in place ## pt &= m # transforms pt in place
## ##
## var pt2:TPoint2d=pt & m #concatenates pt with m and returns a new point ## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
## ##
## var vec2:TVector2d=vec & m #concatenates vec with m and returns a new vector ## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
const const
@ -57,46 +57,46 @@ const
## used internally by DegToRad and RadToDeg ## used internally by DegToRad and RadToDeg
type type
TMatrix2d* = object Matrix2d* = object
## Implements a row major 2d matrix, which means ## Implements a row major 2d matrix, which means
## transformations are applied the order they are concatenated. ## transformations are applied the order they are concatenated.
## The rightmost column of the 3x3 matrix is left out since normally ## The rightmost column of the 3x3 matrix is left out since normally
## not used for geometric transformations in 2d. ## not used for geometric transformations in 2d.
ax*,ay*,bx*,by*,tx*,ty*:float ax*,ay*,bx*,by*,tx*,ty*:float
TPoint2d* = object Point2d* = object
## Implements a non-homegeneous 2d point stored as ## Implements a non-homegeneous 2d point stored as
## an `x` coordinate and an `y` coordinate. ## an `x` coordinate and an `y` coordinate.
x*,y*:float x*,y*:float
TVector2d* = object Vector2d* = object
## Implements a 2d **direction vector** stored as ## Implements a 2d **direction vector** stored as
## an `x` coordinate and an `y` coordinate. Direction vector means, ## an `x` coordinate and an `y` coordinate. Direction vector means,
## that when transforming a vector with a matrix, the translational ## that when transforming a vector with a matrix, the translational
## part of the matrix is ignored. ## part of the matrix is ignored.
x*,y*:float x*,y*:float
{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].}
# Some forward declarations... # Some forward declarations...
proc matrix2d*(ax,ay,bx,by,tx,ty:float):TMatrix2d {.noInit.} proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
## Creates a new matrix. ## Creates a new matrix.
## `ax`,`ay` is the local x axis ## `ax`,`ay` is the local x axis
## `bx`,`by` is the local y axis ## `bx`,`by` is the local y axis
## `tx`,`ty` is the translation ## `tx`,`ty` is the translation
proc vector2d*(x,y:float):TVector2d {.noInit,inline.} proc vector2d*(x,y:float):Vector2d {.noInit,inline.}
## Returns a new vector (`x`,`y`) ## Returns a new vector (`x`,`y`)
proc point2d*(x,y:float):TPoint2d {.noInit,inline.} proc point2d*(x,y:float):Point2d {.noInit,inline.}
## Returns a new point (`x`,`y`) ## Returns a new point (`x`,`y`)
let let
IDMATRIX*:TMatrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0) IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
## Quick access to an identity matrix ## Quick access to an identity matrix
ORIGO*:TPoint2d=point2d(0.0,0.0) ORIGO*:Point2d=point2d(0.0,0.0)
## Quick acces to point (0,0) ## Quick acces to point (0,0)
XAXIS*:TVector2d=vector2d(1.0,0.0) XAXIS*:Vector2d=vector2d(1.0,0.0)
## Quick acces to an 2d x-axis unit vector ## Quick acces to an 2d x-axis unit vector
YAXIS*:TVector2d=vector2d(0.0,1.0) YAXIS*:Vector2d=vector2d(0.0,1.0)
## Quick acces to an 2d y-axis unit vector ## Quick acces to an 2d y-axis unit vector
@ -116,21 +116,21 @@ proc safeArccos(v:float):float=
template makeBinOpVector(s:expr)= template makeBinOpVector(s:expr)=
## implements binary operators + , - , * and / for vectors ## implements binary operators + , - , * and / for vectors
proc s*(a,b:TVector2d):TVector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y)) proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
proc s*(a:TVector2d,b:float):TVector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b)) proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
proc s*(a:float,b:TVector2d):TVector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y)) proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
template makeBinOpAssignVector(s:expr)= template makeBinOpAssignVector(s:expr)=
## implements inplace binary operators += , -= , /= and *= for vectors ## implements inplace binary operators += , -= , /= and *= for vectors
proc s*(a:var TVector2d,b:TVector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y) proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
proc s*(a:var TVector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b) proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
# *************************************** # ***************************************
# TMatrix2d implementation # Matrix2d implementation
# *************************************** # ***************************************
proc setElements*(t:var TMatrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}= proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
## Sets arbitrary elements in an existing matrix. ## Sets arbitrary elements in an existing matrix.
t.ax=ax t.ax=ax
t.ay=ay t.ay=ay
@ -139,10 +139,10 @@ proc setElements*(t:var TMatrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
t.tx=tx t.tx=tx
t.ty=ty t.ty=ty
proc matrix2d*(ax,ay,bx,by,tx,ty:float):TMatrix2d = proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
result.setElements(ax,ay,bx,by,tx,ty) result.setElements(ax,ay,bx,by,tx,ty)
proc `&`*(a,b:TMatrix2d):TMatrix2d {.noInit.} = #concatenate matrices proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
## Concatenates matrices returning a new matrix. ## Concatenates matrices returning a new matrix.
# | a.AX a.AY 0 | | b.AX b.AY 0 | # | a.AX a.AY 0 | | b.AX b.AY 0 |
@ -157,34 +157,34 @@ proc `&`*(a,b:TMatrix2d):TMatrix2d {.noInit.} = #concatenate matrices
a.tx * b.ay + a.ty * b.by + b.ty) a.tx * b.ay + a.ty * b.by + b.ty)
proc scale*(s:float):TMatrix2d {.noInit.} = proc scale*(s:float):Matrix2d {.noInit.} =
## Returns a new scale matrix. ## Returns a new scale matrix.
result.setElements(s,0,0,s,0,0) result.setElements(s,0,0,s,0,0)
proc scale*(s:float,org:TPoint2d):TMatrix2d {.noInit.} = proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} =
## Returns a new scale matrix using, `org` as scale origin. ## Returns a new scale matrix using, `org` as scale origin.
result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y) result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y)
proc stretch*(sx,sy:float):TMatrix2d {.noInit.} = proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
## Returns new a stretch matrix, which is a ## Returns new a stretch matrix, which is a
## scale matrix with non uniform scale in x and y. ## scale matrix with non uniform scale in x and y.
result.setElements(sx,0,0,sy,0,0) result.setElements(sx,0,0,sy,0,0)
proc stretch*(sx,sy:float,org:TPoint2d):TMatrix2d {.noInit.} = proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
## Returns a new stretch matrix, which is a ## Returns a new stretch matrix, which is a
## scale matrix with non uniform scale in x and y. ## scale matrix with non uniform scale in x and y.
## `org` is used as stretch origin. ## `org` is used as stretch origin.
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y) result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
proc move*(dx,dy:float):TMatrix2d {.noInit.} = proc move*(dx,dy:float):Matrix2d {.noInit.} =
## Returns a new translation matrix. ## Returns a new translation matrix.
result.setElements(1,0,0,1,dx,dy) result.setElements(1,0,0,1,dx,dy)
proc move*(v:TVector2d):TMatrix2d {.noInit.} = proc move*(v:Vector2d):Matrix2d {.noInit.} =
## Returns a new translation matrix from a vector. ## Returns a new translation matrix from a vector.
result.setElements(1,0,0,1,v.x,v.y) result.setElements(1,0,0,1,v.x,v.y)
proc rotate*(rad:float):TMatrix2d {.noInit.} = proc rotate*(rad:float):Matrix2d {.noInit.} =
## Returns a new rotation matrix, which ## Returns a new rotation matrix, which
## represents a rotation by `rad` radians ## represents a rotation by `rad` radians
let let
@ -192,7 +192,7 @@ proc rotate*(rad:float):TMatrix2d {.noInit.} =
c=cos(rad) c=cos(rad)
result.setElements(c,s,-s,c,0,0) result.setElements(c,s,-s,c,0,0)
proc rotate*(rad:float,org:TPoint2d):TMatrix2d {.noInit.} = proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
## Returns a new rotation matrix, which ## Returns a new rotation matrix, which
## represents a rotation by `rad` radians around ## represents a rotation by `rad` radians around
## the origin `org` ## the origin `org`
@ -201,7 +201,7 @@ proc rotate*(rad:float,org:TPoint2d):TMatrix2d {.noInit.} =
c=cos(rad) c=cos(rad)
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x) result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
proc mirror*(v:TVector2d):TMatrix2d {.noInit.} = proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
## Returns a new mirror matrix, mirroring ## Returns a new mirror matrix, mirroring
## around the line that passes through origo and ## around the line that passes through origo and
## has the direction of `v` ## has the direction of `v`
@ -220,7 +220,7 @@ proc mirror*(v:TVector2d):TMatrix2d {.noInit.} =
xy2,-sqd, xy2,-sqd,
0.0,0.0) 0.0,0.0)
proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} = proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
## Returns a new mirror matrix, mirroring ## Returns a new mirror matrix, mirroring
## around the line that passes through `org` and ## around the line that passes through `org` and
## has the direction of `v` ## has the direction of `v`
@ -241,20 +241,20 @@ proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} =
proc skew*(xskew,yskew:float):TMatrix2d {.noInit.} = proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
## Returns a new skew matrix, which has its ## Returns a new skew matrix, which has its
## x axis rotated `xskew` radians from the local x axis, and ## x axis rotated `xskew` radians from the local x axis, and
## y axis rotated `yskew` radians from the local y axis ## y axis rotated `yskew` radians from the local y axis
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0) result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
proc `$`* (t:TMatrix2d):string {.noInit.} = proc `$`* (t:Matrix2d):string {.noInit.} =
## Returns a string representation of the matrix ## Returns a string representation of the matrix
return rtos(t.ax) & "," & rtos(t.ay) & return rtos(t.ax) & "," & rtos(t.ay) &
"," & rtos(t.bx) & "," & rtos(t.by) & "," & rtos(t.bx) & "," & rtos(t.by) &
"," & rtos(t.tx) & "," & rtos(t.ty) "," & rtos(t.tx) & "," & rtos(t.ty)
proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool= proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
## Checks if the transform is uniform, that is ## Checks if the transform is uniform, that is
## perpendicular axes of equal length, which means (for example) ## perpendicular axes of equal length, which means (for example)
## it cannot transform a circle into an ellipse. ## it cannot transform a circle into an ellipse.
@ -268,18 +268,18 @@ proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool=
return true return true
return false return false
proc determinant*(t:TMatrix2d):float= proc determinant*(t:Matrix2d):float=
## Computes the determinant of the matrix. ## Computes the determinant of the matrix.
#NOTE: equivalent with perp.dot product for two 2d vectors #NOTE: equivalent with perp.dot product for two 2d vectors
return t.ax*t.by-t.bx*t.ay return t.ax*t.by-t.bx*t.ay
proc isMirroring* (m:TMatrix2d):bool= proc isMirroring* (m:Matrix2d):bool=
## Checks if the `m` is a mirroring matrix, ## Checks if the `m` is a mirroring matrix,
## which means it will reverse direction of a curve transformed with it ## which means it will reverse direction of a curve transformed with it
return m.determinant<0.0 return m.determinant<0.0
proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} = proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
## Returns a new matrix, which is the inverse of the matrix ## Returns a new matrix, which is the inverse of the matrix
## If the matrix is not invertible (determinant=0), an EDivByZero ## If the matrix is not invertible (determinant=0), an EDivByZero
## will be raised. ## will be raised.
@ -293,7 +293,7 @@ proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} =
(m.bx*m.ty-m.by*m.tx)/d, (m.bx*m.ty-m.by*m.tx)/d,
(m.ay*m.tx-m.ax*m.ty)/d) (m.ay*m.tx-m.ax*m.ty)/d)
proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool= proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
## Checks if all elements of `m1`and `m2` is equal within ## Checks if all elements of `m1`and `m2` is equal within
## a given tolerance `tol`. ## a given tolerance `tol`.
return return
@ -304,17 +304,17 @@ proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool=
abs(m1.tx-m2.tx)<=tol and abs(m1.tx-m2.tx)<=tol and
abs(m1.ty-m2.ty)<=tol abs(m1.ty-m2.ty)<=tol
proc `=~`*(m1,m2:TMatrix2d):bool= proc `=~`*(m1,m2:Matrix2d):bool=
## Checks if `m1`and `m2` is approximately equal, using a ## Checks if `m1`and `m2` is approximately equal, using a
## tolerance of 1e-6. ## tolerance of 1e-6.
equals(m1,m2) equals(m1,m2)
proc isIdentity*(m:TMatrix2d,tol=1.0e-6):bool= proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool=
## Checks is a matrix is approximately an identity matrix, ## Checks is a matrix is approximately an identity matrix,
## using `tol` as tolerance for each element. ## using `tol` as tolerance for each element.
return equals(m,IDMATRIX,tol) return equals(m,IDMATRIX,tol)
proc apply*(m:TMatrix2d,x,y:var float,translate=false)= proc apply*(m:Matrix2d,x,y:var float,translate=false)=
## Applies transformation `m` onto `x`,`y`, optionally ## Applies transformation `m` onto `x`,`y`, optionally
## using the translation part of the matrix. ## using the translation part of the matrix.
if translate: # positional style transform if translate: # positional style transform
@ -329,29 +329,29 @@ proc apply*(m:TMatrix2d,x,y:var float,translate=false)=
# *************************************** # ***************************************
# TVector2d implementation # Vector2d implementation
# *************************************** # ***************************************
proc vector2d*(x,y:float):TVector2d = #forward decl. proc vector2d*(x,y:float):Vector2d = #forward decl.
result.x=x result.x=x
result.y=y result.y=y
proc polarVector2d*(ang:float,len:float):TVector2d {.noInit.} = proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} =
## Returns a new vector with angle `ang` and magnitude `len` ## Returns a new vector with angle `ang` and magnitude `len`
result.x=cos(ang)*len result.x=cos(ang)*len
result.y=sin(ang)*len result.y=sin(ang)*len
proc slopeVector2d*(slope:float,len:float):TVector2d {.noInit.} = proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
## Returns a new vector having slope (dy/dx) given by ## Returns a new vector having slope (dy/dx) given by
## `slope`, and a magnitude of `len` ## `slope`, and a magnitude of `len`
let ang=arctan(slope) let ang=arctan(slope)
result.x=cos(ang)*len result.x=cos(ang)*len
result.y=sin(ang)*len result.y=sin(ang)*len
proc len*(v:TVector2d):float {.inline.}= proc len*(v:Vector2d):float {.inline.}=
## Returns the length of the vector. ## Returns the length of the vector.
sqrt(v.x*v.x+v.y*v.y) sqrt(v.x*v.x+v.y*v.y)
proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} = proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
## Sets the length of the vector, keeping its angle. ## Sets the length of the vector, keeping its angle.
let fac=newlen/v.len let fac=newlen/v.len
@ -369,25 +369,25 @@ proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} =
v.x*=fac v.x*=fac
v.y*=fac v.y*=fac
proc sqrLen*(v:TVector2d):float {.inline.}= proc sqrLen*(v:Vector2d):float {.inline.}=
## Computes the squared length of the vector, which is ## Computes the squared length of the vector, which is
## faster than computing the absolute length. ## faster than computing the absolute length.
v.x*v.x+v.y*v.y v.x*v.x+v.y*v.y
proc angle*(v:TVector2d):float= proc angle*(v:Vector2d):float=
## Returns the angle of the vector. ## Returns the angle of the vector.
## (The counter clockwise plane angle between posetive x axis and `v`) ## (The counter clockwise plane angle between posetive x axis and `v`)
result=arctan2(v.y,v.x) result=arctan2(v.y,v.x)
if result<0.0: result+=DEG360 if result<0.0: result+=DEG360
proc `$` *(v:TVector2d):string= proc `$` *(v:Vector2d):string=
## String representation of `v` ## String representation of `v`
result=rtos(v.x) result=rtos(v.x)
result.add(",") result.add(",")
result.add(rtos(v.y)) result.add(rtos(v.y))
proc `&` *(v:TVector2d,m:TMatrix2d):TVector2d {.noInit.} = proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
## Concatenate vector `v` with a transformation matrix. ## Concatenate vector `v` with a transformation matrix.
## Transforming a vector ignores the translational part ## Transforming a vector ignores the translational part
## of the matrix. ## of the matrix.
@ -399,7 +399,7 @@ proc `&` *(v:TVector2d,m:TMatrix2d):TVector2d {.noInit.} =
result.y=v.x*m.ay+v.y*m.by result.y=v.x*m.ay+v.y*m.by
proc `&=`*(v:var TVector2d,m:TMatrix2d) {.inline.}= proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
## Applies transformation `m` onto `v` in place. ## Applies transformation `m` onto `v` in place.
## Transforming a vector ignores the translational part ## Transforming a vector ignores the translational part
## of the matrix. ## of the matrix.
@ -412,7 +412,7 @@ proc `&=`*(v:var TVector2d,m:TMatrix2d) {.inline.}=
v.x=newx v.x=newx
proc tryNormalize*(v:var TVector2d):bool= proc tryNormalize*(v:var Vector2d):bool=
## Modifies `v` to have a length of 1.0, keeping its angle. ## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length (and thus no angle), it is left unmodified and ## If `v` has zero length (and thus no angle), it is left unmodified and
## false is returned, otherwise true is returned. ## false is returned, otherwise true is returned.
@ -427,13 +427,13 @@ proc tryNormalize*(v:var TVector2d):bool=
return true return true
proc normalize*(v:var TVector2d) {.inline.}= proc normalize*(v:var Vector2d) {.inline.}=
## Modifies `v` to have a length of 1.0, keeping its angle. ## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length, an EDivByZero will be raised. ## If `v` has zero length, an EDivByZero will be raised.
if not tryNormalize(v): if not tryNormalize(v):
raise newException(DivByZeroError,"Cannot normalize zero length vector") raise newException(DivByZeroError,"Cannot normalize zero length vector")
proc transformNorm*(v:var TVector2d,t:TMatrix2d)= proc transformNorm*(v:var Vector2d,t:Matrix2d)=
## Applies a normal direction transformation `t` onto `v` in place. ## Applies a normal direction transformation `t` onto `v` in place.
## The resulting vector is *not* normalized. Transforming a vector ignores the ## The resulting vector is *not* normalized. Transforming a vector ignores the
## translational part of the matrix. If the matrix is not invertible ## translational part of the matrix. If the matrix is not invertible
@ -452,7 +452,7 @@ proc transformNorm*(v:var TVector2d,t:TMatrix2d)=
v.y = (t.ax*v.y-t.bx*v.x)/d v.y = (t.ax*v.y-t.bx*v.x)/d
v.x = newx v.x = newx
proc transformInv*(v:var TVector2d,t:TMatrix2d)= proc transformInv*(v:var Vector2d,t:Matrix2d)=
## Applies inverse of a transformation `t` to `v` in place. ## Applies inverse of a transformation `t` to `v` in place.
## This is faster than creating an inverse matrix and apply() it. ## This is faster than creating an inverse matrix and apply() it.
## Transforming a vector ignores the translational part ## Transforming a vector ignores the translational part
@ -467,7 +467,7 @@ proc transformInv*(v:var TVector2d,t:TMatrix2d)=
v.y = (t.ax*v.y-t.ay*v.x)/d v.y = (t.ax*v.y-t.ay*v.x)/d
v.x = newx v.x = newx
proc transformNormInv*(v:var TVector2d,t:TMatrix2d)= proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
## Applies an inverse normal direction transformation `t` onto `v` in place. ## Applies an inverse normal direction transformation `t` onto `v` in place.
## This is faster than creating an inverse ## This is faster than creating an inverse
## matrix and transformNorm(...) it. Transforming a vector ignores the ## matrix and transformNorm(...) it. Transforming a vector ignores the
@ -484,25 +484,25 @@ proc transformNormInv*(v:var TVector2d,t:TMatrix2d)=
v.y=t.by*v.y+t.bx*v.x v.y=t.by*v.y+t.bx*v.x
v.x=newx v.x=newx
proc rotate90*(v:var TVector2d) {.inline.}= proc rotate90*(v:var Vector2d) {.inline.}=
## Quickly rotates vector `v` 90 degrees counter clockwise, ## Quickly rotates vector `v` 90 degrees counter clockwise,
## without using any trigonometrics. ## without using any trigonometrics.
swap(v.x,v.y) swap(v.x,v.y)
v.x= -v.x v.x= -v.x
proc rotate180*(v:var TVector2d){.inline.}= proc rotate180*(v:var Vector2d){.inline.}=
## Quickly rotates vector `v` 180 degrees counter clockwise, ## Quickly rotates vector `v` 180 degrees counter clockwise,
## without using any trigonometrics. ## without using any trigonometrics.
v.x= -v.x v.x= -v.x
v.y= -v.y v.y= -v.y
proc rotate270*(v:var TVector2d) {.inline.}= proc rotate270*(v:var Vector2d) {.inline.}=
## Quickly rotates vector `v` 270 degrees counter clockwise, ## Quickly rotates vector `v` 270 degrees counter clockwise,
## without using any trigonometrics. ## without using any trigonometrics.
swap(v.x,v.y) swap(v.x,v.y)
v.y= -v.y v.y= -v.y
proc rotate*(v:var TVector2d,rad:float) = proc rotate*(v:var Vector2d,rad:float) =
## Rotates vector `v` `rad` radians in place. ## Rotates vector `v` `rad` radians in place.
let let
s=sin(rad) s=sin(rad)
@ -511,18 +511,18 @@ proc rotate*(v:var TVector2d,rad:float) =
v.y=c*v.y+s*v.x v.y=c*v.y+s*v.x
v.x=newx v.x=newx
proc scale*(v:var TVector2d,fac:float){.inline.}= proc scale*(v:var Vector2d,fac:float){.inline.}=
## Scales vector `v` `rad` radians in place. ## Scales vector `v` `rad` radians in place.
v.x*=fac v.x*=fac
v.y*=fac v.y*=fac
proc stretch*(v:var TVector2d,facx,facy:float){.inline.}= proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
## Stretches vector `v` `facx` times horizontally, ## Stretches vector `v` `facx` times horizontally,
## and `facy` times vertically. ## and `facy` times vertically.
v.x*=facx v.x*=facx
v.y*=facy v.y*=facy
proc mirror*(v:var TVector2d,mirrvec:TVector2d)= proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
## Mirrors vector `v` using `mirrvec` as mirror direction. ## Mirrors vector `v` using `mirrvec` as mirror direction.
let let
sqx=mirrvec.x*mirrvec.x sqx=mirrvec.x*mirrvec.x
@ -539,7 +539,7 @@ proc mirror*(v:var TVector2d,mirrvec:TVector2d)=
v.x=newx v.x=newx
proc `-` *(v:TVector2d):TVector2d= proc `-` *(v:Vector2d):Vector2d=
## Negates a vector ## Negates a vector
result.x= -v.x result.x= -v.x
result.y= -v.y result.y= -v.y
@ -555,27 +555,27 @@ makeBinOpAssignVector(`*=`)
makeBinOpAssignVector(`/=`) makeBinOpAssignVector(`/=`)
proc dot*(v1,v2:TVector2d):float= proc dot*(v1,v2:Vector2d):float=
## Computes the dot product of two vectors. ## Computes the dot product of two vectors.
## Returns 0.0 if the vectors are perpendicular. ## Returns 0.0 if the vectors are perpendicular.
return v1.x*v2.x+v1.y*v2.y return v1.x*v2.x+v1.y*v2.y
proc cross*(v1,v2:TVector2d):float= proc cross*(v1,v2:Vector2d):float=
## Computes the cross product of two vectors, also called ## Computes the cross product of two vectors, also called
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors ## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
## are parallel. ## are parallel.
return v1.x*v2.y-v1.y*v2.x return v1.x*v2.y-v1.y*v2.x
proc equals*(v1,v2:TVector2d,tol=1.0e-6):bool= proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
## Checks if two vectors approximately equals with a tolerance. ## Checks if two vectors approximately equals with a tolerance.
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
proc `=~` *(v1,v2:TVector2d):bool= proc `=~` *(v1,v2:Vector2d):bool=
## Checks if two vectors approximately equals with a ## Checks if two vectors approximately equals with a
## hardcoded tolerance 1e-6 ## hardcoded tolerance 1e-6
equals(v1,v2) equals(v1,v2)
proc angleTo*(v1,v2:TVector2d):float= proc angleTo*(v1,v2:Vector2d):float=
## Returns the smallest of the two possible angles ## Returns the smallest of the two possible angles
## between `v1` and `v2` in radians. ## between `v1` and `v2` in radians.
var var
@ -585,7 +585,7 @@ proc angleTo*(v1,v2:TVector2d):float=
return 0.0 # zero length vector has zero angle to any other vector return 0.0 # zero length vector has zero angle to any other vector
return safeArccos(dot(nv1,nv2)) return safeArccos(dot(nv1,nv2))
proc angleCCW*(v1,v2:TVector2d):float= proc angleCCW*(v1,v2:Vector2d):float=
## Returns the counter clockwise plane angle from `v1` to `v2`, ## Returns the counter clockwise plane angle from `v1` to `v2`,
## in range 0 - 2*PI ## in range 0 - 2*PI
let a=v1.angleTo(v2) let a=v1.angleTo(v2)
@ -593,7 +593,7 @@ proc angleCCW*(v1,v2:TVector2d):float=
return a return a
return DEG360-a return DEG360-a
proc angleCW*(v1,v2:TVector2d):float= proc angleCW*(v1,v2:Vector2d):float=
## Returns the clockwise plane angle from `v1` to `v2`, ## Returns the clockwise plane angle from `v1` to `v2`,
## in range 0 - 2*PI ## in range 0 - 2*PI
let a=v1.angleTo(v2) let a=v1.angleTo(v2)
@ -601,7 +601,7 @@ proc angleCW*(v1,v2:TVector2d):float=
return a return a
return DEG360-a return DEG360-a
proc turnAngle*(v1,v2:TVector2d):float= proc turnAngle*(v1,v2:Vector2d):float=
## Returns the amount v1 should be rotated (in radians) to equal v2, ## Returns the amount v1 should be rotated (in radians) to equal v2,
## in range -PI to PI ## in range -PI to PI
let a=v1.angleTo(v2) let a=v1.angleTo(v2)
@ -609,7 +609,7 @@ proc turnAngle*(v1,v2:TVector2d):float=
return -a return -a
return a return a
proc bisect*(v1,v2:TVector2d):TVector2d {.noInit.}= proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
## Computes the bisector between v1 and v2 as a normalized vector. ## Computes the bisector between v1 and v2 as a normalized vector.
## If one of the input vectors has zero length, a normalized version ## If one of the input vectors has zero length, a normalized version
## of the other is returned. If both input vectors has zero length, ## of the other is returned. If both input vectors has zero length,
@ -645,24 +645,24 @@ proc bisect*(v1,v2:TVector2d):TVector2d {.noInit.}=
# *************************************** # ***************************************
# TPoint2d implementation # Point2d implementation
# *************************************** # ***************************************
proc point2d*(x,y:float):TPoint2d = proc point2d*(x,y:float):Point2d =
result.x=x result.x=x
result.y=y result.y=y
proc sqrDist*(a,b:TPoint2d):float= proc sqrDist*(a,b:Point2d):float=
## Computes the squared distance between `a` and `b` ## Computes the squared distance between `a` and `b`
let dx=b.x-a.x let dx=b.x-a.x
let dy=b.y-a.y let dy=b.y-a.y
result=dx*dx+dy*dy result=dx*dx+dy*dy
proc dist*(a,b:TPoint2d):float {.inline.}= proc dist*(a,b:Point2d):float {.inline.}=
## Computes the absolute distance between `a` and `b` ## Computes the absolute distance between `a` and `b`
result=sqrt(sqrDist(a,b)) result=sqrt(sqrDist(a,b))
proc angle*(a,b:TPoint2d):float= proc angle*(a,b:Point2d):float=
## Computes the angle of the vector `b`-`a` ## Computes the angle of the vector `b`-`a`
let dx=b.x-a.x let dx=b.x-a.x
let dy=b.y-a.y let dy=b.y-a.y
@ -670,13 +670,13 @@ proc angle*(a,b:TPoint2d):float=
if result<0: if result<0:
result += DEG360 result += DEG360
proc `$` *(p:TPoint2d):string= proc `$` *(p:Point2d):string=
## String representation of `p` ## String representation of `p`
result=rtos(p.x) result=rtos(p.x)
result.add(",") result.add(",")
result.add(rtos(p.y)) result.add(rtos(p.y))
proc `&`*(p:TPoint2d,t:TMatrix2d):TPoint2d {.noInit,inline.} = proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
## Concatenates a point `p` with a transform `t`, ## Concatenates a point `p` with a transform `t`,
## resulting in a new, transformed point. ## resulting in a new, transformed point.
@ -686,14 +686,14 @@ proc `&`*(p:TPoint2d,t:TMatrix2d):TPoint2d {.noInit,inline.} =
result.x=p.x*t.ax+p.y*t.bx+t.tx result.x=p.x*t.ax+p.y*t.bx+t.tx
result.y=p.x*t.ay+p.y*t.by+t.ty result.y=p.x*t.ay+p.y*t.by+t.ty
proc `&=` *(p:var TPoint2d,t:TMatrix2d) {.inline.}= proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}=
## Applies transformation `t` onto `p` in place. ## Applies transformation `t` onto `p` in place.
let newx=p.x*t.ax+p.y*t.bx+t.tx let newx=p.x*t.ax+p.y*t.bx+t.tx
p.y=p.x*t.ay+p.y*t.by+t.ty p.y=p.x*t.ay+p.y*t.by+t.ty
p.x=newx p.x=newx
proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}= proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
## Applies the inverse of transformation `t` onto `p` in place. ## Applies the inverse of transformation `t` onto `p` in place.
## If the matrix is not invertable (determinant=0) , EDivByZero will ## If the matrix is not invertable (determinant=0) , EDivByZero will
## be raised. ## be raised.
@ -710,48 +710,48 @@ proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}=
p.x=newx p.x=newx
proc `+`*(p:TPoint2d,v:TVector2d):TPoint2d {.noInit,inline.} = proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
## Adds a vector `v` to a point `p`, resulting ## Adds a vector `v` to a point `p`, resulting
## in a new point. ## in a new point.
result.x=p.x+v.x result.x=p.x+v.x
result.y=p.y+v.y result.y=p.y+v.y
proc `+=`*(p:var TPoint2d,v:TVector2d) {.noInit,inline.} = proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
## Adds a vector `v` to a point `p` in place. ## Adds a vector `v` to a point `p` in place.
p.x+=v.x p.x+=v.x
p.y+=v.y p.y+=v.y
proc `-`*(p:TPoint2d,v:TVector2d):TPoint2d {.noInit,inline.} = proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
## Subtracts a vector `v` from a point `p`, resulting ## Subtracts a vector `v` from a point `p`, resulting
## in a new point. ## in a new point.
result.x=p.x-v.x result.x=p.x-v.x
result.y=p.y-v.y result.y=p.y-v.y
proc `-`*(p1,p2:TPoint2d):TVector2d {.noInit,inline.} = proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} =
## Subtracts `p2`from `p1` resulting in a difference vector. ## Subtracts `p2`from `p1` resulting in a difference vector.
result.x=p1.x-p2.x result.x=p1.x-p2.x
result.y=p1.y-p2.y result.y=p1.y-p2.y
proc `-=`*(p:var TPoint2d,v:TVector2d) {.noInit,inline.} = proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
## Subtracts a vector `v` from a point `p` in place. ## Subtracts a vector `v` from a point `p` in place.
p.x-=v.x p.x-=v.x
p.y-=v.y p.y-=v.y
proc equals(p1,p2:TPoint2d,tol=1.0e-6):bool {.inline.}= proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
## Checks if two points approximately equals with a tolerance. ## Checks if two points approximately equals with a tolerance.
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
proc `=~`*(p1,p2:TPoint2d):bool {.inline.}= proc `=~`*(p1,p2:Point2d):bool {.inline.}=
## Checks if two vectors approximately equals with a ## Checks if two vectors approximately equals with a
## hardcoded tolerance 1e-6 ## hardcoded tolerance 1e-6
equals(p1,p2) equals(p1,p2)
proc polar*(p:TPoint2d,ang,dist:float):TPoint2d {.noInit.} = proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} =
## Returns a point with a given angle and distance away from `p` ## Returns a point with a given angle and distance away from `p`
result.x=p.x+cos(ang)*dist result.x=p.x+cos(ang)*dist
result.y=p.y+sin(ang)*dist result.y=p.y+sin(ang)*dist
proc rotate*(p:var TPoint2d,rad:float)= proc rotate*(p:var Point2d,rad:float)=
## Rotates a point in place `rad` radians around origo. ## Rotates a point in place `rad` radians around origo.
let let
c=cos(rad) c=cos(rad)
@ -760,7 +760,7 @@ proc rotate*(p:var TPoint2d,rad:float)=
p.y=p.y*c+p.x*s p.y=p.y*c+p.x*s
p.x=newx p.x=newx
proc rotate*(p:var TPoint2d,rad:float,org:TPoint2d)= proc rotate*(p:var Point2d,rad:float,org:Point2d)=
## Rotates a point in place `rad` radians using `org` as ## Rotates a point in place `rad` radians using `org` as
## center of rotation. ## center of rotation.
let let
@ -770,50 +770,50 @@ proc rotate*(p:var TPoint2d,rad:float,org:TPoint2d)=
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
p.x=newx p.x=newx
proc scale*(p:var TPoint2d,fac:float) {.inline.}= proc scale*(p:var Point2d,fac:float) {.inline.}=
## Scales a point in place `fac` times with world origo as origin. ## Scales a point in place `fac` times with world origo as origin.
p.x*=fac p.x*=fac
p.y*=fac p.y*=fac
proc scale*(p:var TPoint2d,fac:float,org:TPoint2d){.inline.}= proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
## Scales the point in place `fac` times with `org` as origin. ## Scales the point in place `fac` times with `org` as origin.
p.x=(p.x - org.x) * fac + org.x p.x=(p.x - org.x) * fac + org.x
p.y=(p.y - org.y) * fac + org.y p.y=(p.y - org.y) * fac + org.y
proc stretch*(p:var TPoint2d,facx,facy:float){.inline.}= proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
## Scales a point in place non uniformly `facx` and `facy` times with ## Scales a point in place non uniformly `facx` and `facy` times with
## world origo as origin. ## world origo as origin.
p.x*=facx p.x*=facx
p.y*=facy p.y*=facy
proc stretch*(p:var TPoint2d,facx,facy:float,org:TPoint2d){.inline.}= proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
## Scales the point in place non uniformly `facx` and `facy` times with ## Scales the point in place non uniformly `facx` and `facy` times with
## `org` as origin. ## `org` as origin.
p.x=(p.x - org.x) * facx + org.x p.x=(p.x - org.x) * facx + org.x
p.y=(p.y - org.y) * facy + org.y p.y=(p.y - org.y) * facy + org.y
proc move*(p:var TPoint2d,dx,dy:float){.inline.}= proc move*(p:var Point2d,dx,dy:float){.inline.}=
## Translates a point `dx`, `dy` in place. ## Translates a point `dx`, `dy` in place.
p.x+=dx p.x+=dx
p.y+=dy p.y+=dy
proc move*(p:var TPoint2d,v:TVector2d){.inline.}= proc move*(p:var Point2d,v:Vector2d){.inline.}=
## Translates a point with vector `v` in place. ## Translates a point with vector `v` in place.
p.x+=v.x p.x+=v.x
p.y+=v.y p.y+=v.y
proc sgnArea*(a,b,c:TPoint2d):float= proc sgnArea*(a,b,c:Point2d):float=
## Computes the signed area of the triangle thru points `a`,`b` and `c` ## Computes the signed area of the triangle thru points `a`,`b` and `c`
## result>0.0 for counter clockwise triangle ## result>0.0 for counter clockwise triangle
## result<0.0 for clockwise triangle ## result<0.0 for clockwise triangle
## This is commonly used to determinate side of a point with respect to a line. ## This is commonly used to determinate side of a point with respect to a line.
return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5 return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5
proc area*(a,b,c:TPoint2d):float= proc area*(a,b,c:Point2d):float=
## Computes the area of the triangle thru points `a`,`b` and `c` ## Computes the area of the triangle thru points `a`,`b` and `c`
return abs(sgnArea(a,b,c)) return abs(sgnArea(a,b,c))
proc closestPoint*(p:TPoint2d,pts:varargs[TPoint2d]):TPoint2d= proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
## Returns a point selected from `pts`, that has the closest ## Returns a point selected from `pts`, that has the closest
## euclidean distance to `p` ## euclidean distance to `p`
assert(pts.len>0) # must have at least one point assert(pts.len>0) # must have at least one point

View file

@ -25,25 +25,25 @@ import times
## ##
## # Create a matrix which first rotates, then scales and at last translates ## # Create a matrix which first rotates, then scales and at last translates
## ##
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0) ## var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
## ##
## # Create a 3d point at (100,150,200) and a vector (5,2,3) ## # Create a 3d point at (100,150,200) and a vector (5,2,3)
## ##
## var pt:TPoint3d=point3d(100.0,150.0,200.0) ## var pt:Point3d=point3d(100.0,150.0,200.0)
## ##
## var vec:TVector3d=vector3d(5.0,2.0,3.0) ## var vec:Vector3d=vector3d(5.0,2.0,3.0)
## ##
## ##
## pt &= m # transforms pt in place ## pt &= m # transforms pt in place
## ##
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point ## var pt2:Point3d=pt & m #concatenates pt with m and returns a new point
## ##
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector ## var vec2:Vector3d=vec & m #concatenates vec with m and returns a new vector
type type
TMatrix3d* =object Matrix3d* =object
## Implements a row major 3d matrix, which means ## Implements a row major 3d matrix, which means
## transformations are applied the order they are concatenated. ## transformations are applied the order they are concatenated.
## This matrix is stored as an 4x4 matrix: ## This matrix is stored as an 4x4 matrix:
@ -52,31 +52,31 @@ type
## [ cx cy cz cw ] ## [ cx cy cz cw ]
## [ tx ty tz tw ] ## [ tx ty tz tw ]
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
TPoint3d* = object Point3d* = object
## Implements a non-homegeneous 2d point stored as ## Implements a non-homegeneous 2d point stored as
## an `x` , `y` and `z` coordinate. ## an `x` , `y` and `z` coordinate.
x*,y*,z*:float x*,y*,z*:float
TVector3d* = object Vector3d* = object
## Implements a 3d **direction vector** stored as ## Implements a 3d **direction vector** stored as
## an `x` , `y` and `z` coordinate. Direction vector means, ## an `x` , `y` and `z` coordinate. Direction vector means,
## that when transforming a vector with a matrix, the translational ## that when transforming a vector with a matrix, the translational
## part of the matrix is ignored. ## part of the matrix is ignored.
x*,y*,z*:float x*,y*,z*:float
{.deprecated: [TMatrix3d: Matrix3d, TPoint3d: Point3d, TVector3d: Vector3d].}
# Some forward declarations # Some forward declarations
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.} proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d {.noInit.}
## Creates a new 4x4 3d transformation matrix. ## Creates a new 4x4 3d transformation matrix.
## `ax` , `ay` , `az` is the local x axis. ## `ax` , `ay` , `az` is the local x axis.
## `bx` , `by` , `bz` is the local y axis. ## `bx` , `by` , `bz` is the local y axis.
## `cx` , `cy` , `cz` is the local z axis. ## `cx` , `cy` , `cz` is the local z axis.
## `tx` , `ty` , `tz` is the translation. ## `tx` , `ty` , `tz` is the translation.
proc vector3d*(x,y,z:float):TVector3d {.noInit,inline.} proc vector3d*(x,y,z:float):Vector3d {.noInit,inline.}
## Returns a new 3d vector (`x`,`y`,`z`) ## Returns a new 3d vector (`x`,`y`,`z`)
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.} proc point3d*(x,y,z:float):Point3d {.noInit,inline.}
## Returns a new 4d point (`x`,`y`,`z`) ## Returns a new 4d point (`x`,`y`,`z`)
proc tryNormalize*(v:var TVector3d):bool proc tryNormalize*(v:var Vector3d):bool
## Modifies `v` to have a length of 1.0, keeping its angle. ## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length (and thus no angle), it is left unmodified and false is ## If `v` has zero length (and thus no angle), it is left unmodified and false is
## returned, otherwise true is returned. ## returned, otherwise true is returned.
@ -84,19 +84,19 @@ proc tryNormalize*(v:var TVector3d):bool
let let
IDMATRIX*:TMatrix3d=matrix3d( IDMATRIX*:Matrix3d=matrix3d(
1.0,0.0,0.0,0.0, 1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0, 0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0, 0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0) 0.0,0.0,0.0,1.0)
## Quick access to a 3d identity matrix ## Quick access to a 3d identity matrix
ORIGO*:TPoint3d=point3d(0.0,0.0,0.0) ORIGO*:Point3d=point3d(0.0,0.0,0.0)
## Quick access to point (0,0) ## Quick access to point (0,0)
XAXIS*:TVector3d=vector3d(1.0,0.0,0.0) XAXIS*:Vector3d=vector3d(1.0,0.0,0.0)
## Quick access to an 3d x-axis unit vector ## Quick access to an 3d x-axis unit vector
YAXIS*:TVector3d=vector3d(0.0,1.0,0.0) YAXIS*:Vector3d=vector3d(0.0,1.0,0.0)
## Quick access to an 3d y-axis unit vector ## Quick access to an 3d y-axis unit vector
ZAXIS*:TVector3d=vector3d(0.0,0.0,1.0) ZAXIS*:Vector3d=vector3d(0.0,0.0,1.0)
## Quick access to an 3d z-axis unit vector ## Quick access to an 3d z-axis unit vector
@ -116,27 +116,27 @@ proc safeArccos(v:float):float=
template makeBinOpVector(s:expr)= template makeBinOpVector(s:expr)=
## implements binary operators + , - , * and / for vectors ## implements binary operators + , - , * and / for vectors
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} = proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} =
vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z)) vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} = proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.} =
vector3d(s(a.x,b),s(a.y,b),s(a.z,b)) vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} = proc s*(a:float,b:Vector3d):Vector3d {.inline,noInit.} =
vector3d(s(a,b.x),s(a,b.y),s(a,b.z)) vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
template makeBinOpAssignVector(s:expr)= template makeBinOpAssignVector(s:expr)=
## implements inplace binary operators += , -= , /= and *= for vectors ## implements inplace binary operators += , -= , /= and *= for vectors
proc s*(a:var TVector3d,b:TVector3d) {.inline.} = proc s*(a:var Vector3d,b:Vector3d) {.inline.} =
s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z) s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
proc s*(a:var TVector3d,b:float) {.inline.} = proc s*(a:var Vector3d,b:float) {.inline.} =
s(a.x,b) ; s(a.y,b) ; s(a.z,b) s(a.x,b) ; s(a.y,b) ; s(a.z,b)
# *************************************** # ***************************************
# TMatrix3d implementation # Matrix3d implementation
# *************************************** # ***************************************
proc setElements*(t:var TMatrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}= proc setElements*(t:var Matrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}=
## Sets arbitrary elements in an exisitng matrix. ## Sets arbitrary elements in an exisitng matrix.
t.ax=ax t.ax=ax
t.ay=ay t.ay=ay
@ -155,10 +155,10 @@ proc setElements*(t:var TMatrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,t
t.tz=tz t.tz=tz
t.tw=tw t.tw=tw
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d = proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d =
result.setElements(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw) result.setElements(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw)
proc `&`*(a,b:TMatrix3d):TMatrix3d {.noinit.} = proc `&`*(a,b:Matrix3d):Matrix3d {.noinit.} =
## Concatenates matrices returning a new matrix. ## Concatenates matrices returning a new matrix.
result.setElements( result.setElements(
a.aw*b.tx+a.az*b.cx+a.ay*b.bx+a.ax*b.ax, a.aw*b.tx+a.az*b.cx+a.ay*b.bx+a.ax*b.ax,
@ -182,36 +182,36 @@ proc `&`*(a,b:TMatrix3d):TMatrix3d {.noinit.} =
a.tw*b.tw+a.tz*b.cw+a.ty*b.bw+a.tx*b.aw) a.tw*b.tw+a.tz*b.cw+a.ty*b.bw+a.tx*b.aw)
proc scale*(s:float):TMatrix3d {.noInit.} = proc scale*(s:float):Matrix3d {.noInit.} =
## Returns a new scaling matrix. ## Returns a new scaling matrix.
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, 0,0,0,1) result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, 0,0,0,1)
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} = proc scale*(s:float,org:Point3d):Matrix3d {.noInit.} =
## Returns a new scaling matrix using, `org` as scale origin. ## Returns a new scaling matrix using, `org` as scale origin.
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0) org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} = proc stretch*(sx,sy,sz:float):Matrix3d {.noInit.} =
## Returns new a stretch matrix, which is a ## Returns new a stretch matrix, which is a
## scale matrix with non uniform scale in x,y and z. ## scale matrix with non uniform scale in x,y and z.
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1) result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} = proc stretch*(sx,sy,sz:float,org:Point3d):Matrix3d {.noInit.} =
## Returns a new stretch matrix, which is a ## Returns a new stretch matrix, which is a
## scale matrix with non uniform scale in x,y and z. ## scale matrix with non uniform scale in x,y and z.
## `org` is used as stretch origin. ## `org` is used as stretch origin.
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1) result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} = proc move*(dx,dy,dz:float):Matrix3d {.noInit.} =
## Returns a new translation matrix. ## Returns a new translation matrix.
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1) result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
proc move*(v:TVector3d):TMatrix3d {.noInit.} = proc move*(v:Vector3d):Matrix3d {.noInit.} =
## Returns a new translation matrix from a vector. ## Returns a new translation matrix from a vector.
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, v.x,v.y,v.z,1) result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, v.x,v.y,v.z,1)
proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}= proc rotate*(angle:float,axis:Vector3d):Matrix3d {.noInit.}=
## Creates a rotation matrix that rotates `angle` radians over ## Creates a rotation matrix that rotates `angle` radians over
## `axis`, which passes through origo. ## `axis`, which passes through origo.
@ -242,7 +242,7 @@ proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
uwomc+vsi, vwomc-usi, w2+(1.0-w2)*cs, 0.0, uwomc+vsi, vwomc-usi, w2+(1.0-w2)*cs, 0.0,
0.0,0.0,0.0,1.0) 0.0,0.0,0.0,1.0)
proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}= proc rotate*(angle:float,org:Point3d,axis:Vector3d):Matrix3d {.noInit.}=
## Creates a rotation matrix that rotates `angle` radians over ## Creates a rotation matrix that rotates `angle` radians over
## `axis`, which passes through `org`. ## `axis`, which passes through `org`.
@ -282,7 +282,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
(c*(u2+v2)-w*(a*u+b*v))*omc+(a*v-b*u)*si,1.0) (c*(u2+v2)-w*(a*u+b*v))*omc+(a*v-b*u)*si,1.0)
proc rotateX*(angle:float):TMatrix3d {.noInit.}= proc rotateX*(angle:float):Matrix3d {.noInit.}=
## Creates a matrix that rotates around the x-axis with `angle` radians, ## Creates a matrix that rotates around the x-axis with `angle` radians,
## which is also called a 'roll' matrix. ## which is also called a 'roll' matrix.
let let
@ -294,7 +294,7 @@ proc rotateX*(angle:float):TMatrix3d {.noInit.}=
0,-s,c,0, 0,-s,c,0,
0,0,0,1) 0,0,0,1)
proc rotateY*(angle:float):TMatrix3d {.noInit.}= proc rotateY*(angle:float):Matrix3d {.noInit.}=
## Creates a matrix that rotates around the y-axis with `angle` radians, ## Creates a matrix that rotates around the y-axis with `angle` radians,
## which is also called a 'pitch' matrix. ## which is also called a 'pitch' matrix.
let let
@ -306,7 +306,7 @@ proc rotateY*(angle:float):TMatrix3d {.noInit.}=
s,0,c,0, s,0,c,0,
0,0,0,1) 0,0,0,1)
proc rotateZ*(angle:float):TMatrix3d {.noInit.}= proc rotateZ*(angle:float):Matrix3d {.noInit.}=
## Creates a matrix that rotates around the z-axis with `angle` radians, ## Creates a matrix that rotates around the z-axis with `angle` radians,
## which is also called a 'yaw' matrix. ## which is also called a 'yaw' matrix.
let let
@ -318,7 +318,7 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
0,0,1,0, 0,0,1,0,
0,0,0,1) 0,0,0,1)
proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool= proc isUniform*(m:Matrix3d,tol=1.0e-6):bool=
## Checks if the transform is uniform, that is ## Checks if the transform is uniform, that is
## perpendicular axes of equal length, which means (for example) ## perpendicular axes of equal length, which means (for example)
## it cannot transform a sphere into an ellipsoid. ## it cannot transform a sphere into an ellipsoid.
@ -341,7 +341,7 @@ proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}= proc mirror*(planeperp:Vector3d):Matrix3d {.noInit.}=
## Creates a matrix that mirrors over the plane that has `planeperp` as normal, ## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
## and passes through origo. `planeperp` does not need to be normalized. ## and passes through origo. `planeperp` does not need to be normalized.
@ -365,7 +365,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
0,0,0,1) 0,0,0,1)
proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}= proc mirror*(org:Point3d,planeperp:Vector3d):Matrix3d {.noInit.}=
## Creates a matrix that mirrors over the plane that has `planeperp` as normal, ## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
## and passes through `org`. `planeperp` does not need to be normalized. ## and passes through `org`. `planeperp` does not need to be normalized.
@ -400,7 +400,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
2*(cc*tz+bc*ty+ac*tx) ,1) 2*(cc*tz+bc*ty+ac*tx) ,1)
proc determinant*(m:TMatrix3d):float= proc determinant*(m:Matrix3d):float=
## Computes the determinant of matrix `m`. ## Computes the determinant of matrix `m`.
# This computation is gotten from ratsimp(optimize(determinant(m))) # This computation is gotten from ratsimp(optimize(determinant(m)))
@ -419,7 +419,7 @@ proc determinant*(m:TMatrix3d):float=
(O3*m.az-O5*m.ay+O6*m.ax)*m.bw (O3*m.az-O5*m.ay+O6*m.ax)*m.bw
proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}= proc inverse*(m:Matrix3d):Matrix3d {.noInit.}=
## Computes the inverse of matrix `m`. If the matrix ## Computes the inverse of matrix `m`. If the matrix
## determinant is zero, thus not invertible, a EDivByZero ## determinant is zero, thus not invertible, a EDivByZero
## will be raised. ## will be raised.
@ -461,7 +461,7 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
(-m.ax*O7+m.ay*O14-m.az*O18)/det , (m.ax*O10-m.ay*O16+m.az*O19)/det) (-m.ax*O7+m.ay*O14-m.az*O18)/det , (m.ax*O10-m.ay*O16+m.az*O19)/det)
proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool= proc equals*(m1:Matrix3d,m2:Matrix3d,tol=1.0e-6):bool=
## Checks if all elements of `m1`and `m2` is equal within ## Checks if all elements of `m1`and `m2` is equal within
## a given tolerance `tol`. ## a given tolerance `tol`.
return return
@ -482,42 +482,42 @@ proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
abs(m1.tz-m2.tz)<=tol and abs(m1.tz-m2.tz)<=tol and
abs(m1.tw-m2.tw)<=tol abs(m1.tw-m2.tw)<=tol
proc `=~`*(m1,m2:TMatrix3d):bool= proc `=~`*(m1,m2:Matrix3d):bool=
## Checks if `m1` and `m2` is approximately equal, using a ## Checks if `m1` and `m2` is approximately equal, using a
## tolerance of 1e-6. ## tolerance of 1e-6.
equals(m1,m2) equals(m1,m2)
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}= proc transpose*(m:Matrix3d):Matrix3d {.noInit.}=
## Returns the transpose of `m` ## Returns the transpose of `m`
result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw) result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}= proc getXAxis*(m:Matrix3d):Vector3d {.noInit.}=
## Gets the local x axis of `m` ## Gets the local x axis of `m`
result.x=m.ax result.x=m.ax
result.y=m.ay result.y=m.ay
result.z=m.az result.z=m.az
proc getYAxis*(m:TMatrix3d):TVector3d {.noInit.}= proc getYAxis*(m:Matrix3d):Vector3d {.noInit.}=
## Gets the local y axis of `m` ## Gets the local y axis of `m`
result.x=m.bx result.x=m.bx
result.y=m.by result.y=m.by
result.z=m.bz result.z=m.bz
proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}= proc getZAxis*(m:Matrix3d):Vector3d {.noInit.}=
## Gets the local y axis of `m` ## Gets the local y axis of `m`
result.x=m.cx result.x=m.cx
result.y=m.cy result.y=m.cy
result.z=m.cz result.z=m.cz
proc `$`*(m:TMatrix3d):string= proc `$`*(m:Matrix3d):string=
## String representation of `m` ## String representation of `m`
return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) & return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) &
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) & "\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) &
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) & "\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw) "\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)= proc apply*(m:Matrix3d, x,y,z:var float, translate=false)=
## Applies transformation `m` onto `x` , `y` , `z` , optionally ## Applies transformation `m` onto `x` , `y` , `z` , optionally
## using the translation part of the matrix. ## using the translation part of the matrix.
let let
@ -535,18 +535,18 @@ proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
z+=m.tz z+=m.tz
# *************************************** # ***************************************
# TVector3d implementation # Vector3d implementation
# *************************************** # ***************************************
proc vector3d*(x,y,z:float):TVector3d= proc vector3d*(x,y,z:float):Vector3d=
result.x=x result.x=x
result.y=y result.y=y
result.z=z result.z=z
proc len*(v:TVector3d):float= proc len*(v:Vector3d):float=
## Returns the length of the vector `v`. ## Returns the length of the vector `v`.
sqrt(v.x*v.x+v.y*v.y+v.z*v.z) sqrt(v.x*v.x+v.y*v.y+v.z*v.z)
proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} = proc `len=`*(v:var Vector3d,newlen:float) {.noInit.} =
## Sets the length of the vector, keeping its direction. ## Sets the length of the vector, keeping its direction.
## If the vector has zero length before changing it's length, ## If the vector has zero length before changing it's length,
## an arbitrary vector of the requested length is returned. ## an arbitrary vector of the requested length is returned.
@ -571,12 +571,12 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
v.z*=fac v.z*=fac
proc sqrLen*(v:TVector3d):float {.inline.}= proc sqrLen*(v:Vector3d):float {.inline.}=
## Computes the squared length of the vector, which is ## Computes the squared length of the vector, which is
## faster than computing the absolute length. ## faster than computing the absolute length.
return v.x*v.x+v.y*v.y+v.z*v.z return v.x*v.x+v.y*v.y+v.z*v.z
proc `$` *(v:TVector3d):string= proc `$` *(v:Vector3d):string=
## String representation of `v` ## String representation of `v`
result=rtos(v.x) result=rtos(v.x)
result.add(",") result.add(",")
@ -584,7 +584,7 @@ proc `$` *(v:TVector3d):string=
result.add(",") result.add(",")
result.add(rtos(v.z)) result.add(rtos(v.z))
proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} = proc `&` *(v:Vector3d,m:Matrix3d):Vector3d {.noInit.} =
## Concatenate vector `v` with a transformation matrix. ## Concatenate vector `v` with a transformation matrix.
## Transforming a vector ignores the translational part ## Transforming a vector ignores the translational part
## of the matrix. ## of the matrix.
@ -601,7 +601,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
result.x=newx result.x=newx
proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} = proc `&=` *(v:var Vector3d,m:Matrix3d) {.noInit.} =
## Applies transformation `m` onto `v` in place. ## Applies transformation `m` onto `v` in place.
## Transforming a vector ignores the translational part ## Transforming a vector ignores the translational part
## of the matrix. ## of the matrix.
@ -618,7 +618,7 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
v.y=newy v.y=newy
v.x=newx v.x=newx
proc transformNorm*(v:var TVector3d,m:TMatrix3d)= proc transformNorm*(v:var Vector3d,m:Matrix3d)=
## Applies a normal direction transformation `m` onto `v` in place. ## Applies a normal direction transformation `m` onto `v` in place.
## The resulting vector is *not* normalized. Transforming a vector ignores the ## The resulting vector is *not* normalized. Transforming a vector ignores the
## translational part of the matrix. If the matrix is not invertible ## translational part of the matrix. If the matrix is not invertible
@ -631,7 +631,7 @@ proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
# (possibly by hardware) as well as having a consistent API with the 2d version. # (possibly by hardware) as well as having a consistent API with the 2d version.
v&=transpose(inverse(m)) v&=transpose(inverse(m))
proc transformInv*(v:var TVector3d,m:TMatrix3d)= proc transformInv*(v:var Vector3d,m:Matrix3d)=
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores ## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
## the translational part of the matrix. Transforming a vector ignores the ## the translational part of the matrix. Transforming a vector ignores the
## translational part of the matrix. ## translational part of the matrix.
@ -642,7 +642,7 @@ proc transformInv*(v:var TVector3d,m:TMatrix3d)=
# (possibly by hardware) as well as having a consistent API with the 2d version. # (possibly by hardware) as well as having a consistent API with the 2d version.
v&=m.inverse v&=m.inverse
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)= proc transformNormInv*(vec:var Vector3d,m:Matrix3d)=
## Applies an inverse normal direction transformation `m` onto `v` in place. ## Applies an inverse normal direction transformation `m` onto `v` in place.
## This is faster than creating an inverse ## This is faster than creating an inverse
## matrix and transformNorm(...) it. Transforming a vector ignores the ## matrix and transformNorm(...) it. Transforming a vector ignores the
@ -651,7 +651,7 @@ proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
# see vector2d:s equivalent for a deeper look how/why this works # see vector2d:s equivalent for a deeper look how/why this works
vec&=m.transpose vec&=m.transpose
proc tryNormalize*(v:var TVector3d):bool= proc tryNormalize*(v:var Vector3d):bool=
## Modifies `v` to have a length of 1.0, keeping its angle. ## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length (and thus no angle), it is left unmodified and false is ## If `v` has zero length (and thus no angle), it is left unmodified and false is
## returned, otherwise true is returned. ## returned, otherwise true is returned.
@ -666,13 +666,13 @@ proc tryNormalize*(v:var TVector3d):bool=
return true return true
proc normalize*(v:var TVector3d) {.inline.}= proc normalize*(v:var Vector3d) {.inline.}=
## Modifies `v` to have a length of 1.0, keeping its angle. ## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length, an EDivByZero will be raised. ## If `v` has zero length, an EDivByZero will be raised.
if not tryNormalize(v): if not tryNormalize(v):
raise newException(DivByZeroError,"Cannot normalize zero length vector") raise newException(DivByZeroError,"Cannot normalize zero length vector")
proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)= proc rotate*(vec:var Vector3d,angle:float,axis:Vector3d)=
## Rotates `vec` in place, with `angle` radians over `axis`, which passes ## Rotates `vec` in place, with `angle` radians over `axis`, which passes
## through origo. ## through origo.
@ -699,19 +699,19 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
proc scale*(v:var TVector3d,s:float)= proc scale*(v:var Vector3d,s:float)=
## Scales the vector in place with factor `s` ## Scales the vector in place with factor `s`
v.x*=s v.x*=s
v.y*=s v.y*=s
v.z*=s v.z*=s
proc stretch*(v:var TVector3d,sx,sy,sz:float)= proc stretch*(v:var Vector3d,sx,sy,sz:float)=
## Scales the vector non uniformly with factors `sx` , `sy` , `sz` ## Scales the vector non uniformly with factors `sx` , `sy` , `sz`
v.x*=sx v.x*=sx
v.y*=sy v.y*=sy
v.z*=sz v.z*=sz
proc mirror*(v:var TVector3d,planeperp:TVector3d)= proc mirror*(v:var Vector3d,planeperp:Vector3d)=
## Computes the mirrored vector of `v` over the plane ## Computes the mirrored vector of `v` over the plane
## that has `planeperp` as normal direction. ## that has `planeperp` as normal direction.
## `planeperp` does not need to be normalized. ## `planeperp` does not need to be normalized.
@ -735,7 +735,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)=
v.z= -2*(c*c*z+bc*y+ac*x)+z v.z= -2*(c*c*z+bc*y+ac*x)+z
proc `-` *(v:TVector3d):TVector3d= proc `-` *(v:Vector3d):Vector3d=
## Negates a vector ## Negates a vector
result.x= -v.x result.x= -v.x
result.y= -v.y result.y= -v.y
@ -751,12 +751,12 @@ makeBinOpAssignVector(`-=`)
makeBinOpAssignVector(`*=`) makeBinOpAssignVector(`*=`)
makeBinOpAssignVector(`/=`) makeBinOpAssignVector(`/=`)
proc dot*(v1,v2:TVector3d):float {.inline.}= proc dot*(v1,v2:Vector3d):float {.inline.}=
## Computes the dot product of two vectors. ## Computes the dot product of two vectors.
## Returns 0.0 if the vectors are perpendicular. ## Returns 0.0 if the vectors are perpendicular.
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
proc cross*(v1,v2:TVector3d):TVector3d {.inline.}= proc cross*(v1,v2:Vector3d):Vector3d {.inline.}=
## Computes the cross product of two vectors. ## Computes the cross product of two vectors.
## The result is a vector which is perpendicular ## The result is a vector which is perpendicular
## to the plane of `v1` and `v2`, which means ## to the plane of `v1` and `v2`, which means
@ -766,16 +766,16 @@ proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
result.y = (v1.z * v2.x) - (v2.z * v1.x) result.y = (v1.z * v2.x) - (v2.z * v1.x)
result.z = (v1.x * v2.y) - (v2.x * v1.y) result.z = (v1.x * v2.y) - (v2.x * v1.y)
proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool= proc equals*(v1,v2:Vector3d,tol=1.0e-6):bool=
## Checks if two vectors approximately equals with a tolerance. ## Checks if two vectors approximately equals with a tolerance.
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
proc `=~` *(v1,v2:TVector3d):bool= proc `=~` *(v1,v2:Vector3d):bool=
## Checks if two vectors approximately equals with a ## Checks if two vectors approximately equals with a
## hardcoded tolerance 1e-6 ## hardcoded tolerance 1e-6
equals(v1,v2) equals(v1,v2)
proc angleTo*(v1,v2:TVector3d):float= proc angleTo*(v1,v2:Vector3d):float=
## Returns the smallest angle between v1 and v2, ## Returns the smallest angle between v1 and v2,
## which is in range 0-PI ## which is in range 0-PI
var var
@ -785,13 +785,13 @@ proc angleTo*(v1,v2:TVector3d):float=
return 0.0 # zero length vector has zero angle to any other vector return 0.0 # zero length vector has zero angle to any other vector
return safeArccos(dot(nv1,nv2)) return safeArccos(dot(nv1,nv2))
proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}= proc arbitraryAxis*(norm:Vector3d):Matrix3d {.noInit.}=
## Computes the rotation matrix that would transform ## Computes the rotation matrix that would transform
## world z vector into `norm`. The inverse of this matrix ## world z vector into `norm`. The inverse of this matrix
## is useful to transform a planar 3d object to 2d space. ## is useful to transform a planar 3d object to 2d space.
## This is the same algorithm used to interpret DXF and DWG files. ## This is the same algorithm used to interpret DXF and DWG files.
const lim=1.0/64.0 const lim=1.0/64.0
var ax,ay,az:TVector3d var ax,ay,az:Vector3d
if abs(norm.x)<lim and abs(norm.y)<lim: if abs(norm.x)<lim and abs(norm.y)<lim:
ax=cross(YAXIS,norm) ax=cross(YAXIS,norm)
else: else:
@ -808,7 +808,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
az.x,az.y,az.z,0.0, az.x,az.y,az.z,0.0,
0.0,0.0,0.0,1.0) 0.0,0.0,0.0,1.0)
proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}= proc bisect*(v1,v2:Vector3d):Vector3d {.noInit.}=
## Computes the bisector between v1 and v2 as a normalized vector. ## Computes the bisector between v1 and v2 as a normalized vector.
## If one of the input vectors has zero length, a normalized version ## If one of the input vectors has zero length, a normalized version
## of the other is returned. If both input vectors has zero length, ## of the other is returned. If both input vectors has zero length,
@ -851,25 +851,25 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
# *************************************** # ***************************************
# TPoint3d implementation # Point3d implementation
# *************************************** # ***************************************
proc point3d*(x,y,z:float):TPoint3d= proc point3d*(x,y,z:float):Point3d=
result.x=x result.x=x
result.y=y result.y=y
result.z=z result.z=z
proc sqrDist*(a,b:TPoint3d):float= proc sqrDist*(a,b:Point3d):float=
## Computes the squared distance between `a`and `b` ## Computes the squared distance between `a`and `b`
let dx=b.x-a.x let dx=b.x-a.x
let dy=b.y-a.y let dy=b.y-a.y
let dz=b.z-a.z let dz=b.z-a.z
result=dx*dx+dy*dy+dz*dz result=dx*dx+dy*dy+dz*dz
proc dist*(a,b:TPoint3d):float {.inline.}= proc dist*(a,b:Point3d):float {.inline.}=
## Computes the absolute distance between `a`and `b` ## Computes the absolute distance between `a`and `b`
result=sqrt(sqrDist(a,b)) result=sqrt(sqrDist(a,b))
proc `$` *(p:TPoint3d):string= proc `$` *(p:Point3d):string=
## String representation of `p` ## String representation of `p`
result=rtos(p.x) result=rtos(p.x)
result.add(",") result.add(",")
@ -877,14 +877,14 @@ proc `$` *(p:TPoint3d):string=
result.add(",") result.add(",")
result.add(rtos(p.z)) result.add(rtos(p.z))
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d= proc `&`*(p:Point3d,m:Matrix3d):Point3d=
## Concatenates a point `p` with a transform `m`, ## Concatenates a point `p` with a transform `m`,
## resulting in a new, transformed point. ## resulting in a new, transformed point.
result.z=m.cz*p.z+m.bz*p.y+m.az*p.x+m.tz result.z=m.cz*p.z+m.bz*p.y+m.az*p.x+m.tz
result.y=m.cy*p.z+m.by*p.y+m.ay*p.x+m.ty result.y=m.cy*p.z+m.by*p.y+m.ay*p.x+m.ty
result.x=m.cx*p.z+m.bx*p.y+m.ax*p.x+m.tx result.x=m.cx*p.z+m.bx*p.y+m.ax*p.x+m.tx
proc `&=` *(p:var TPoint3d,m:TMatrix3d)= proc `&=` *(p:var Point3d,m:Matrix3d)=
## Applies transformation `m` onto `p` in place. ## Applies transformation `m` onto `p` in place.
let let
x=p.x x=p.x
@ -894,7 +894,7 @@ proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
p.y=m.cy*z+m.by*y+m.ay*x+m.ty p.y=m.cy*z+m.by*y+m.ay*x+m.ty
p.z=m.cz*z+m.bz*y+m.az*x+m.tz p.z=m.cz*z+m.bz*y+m.az*x+m.tz
proc transformInv*(p:var TPoint3d,m:TMatrix3d)= proc transformInv*(p:var Point3d,m:Matrix3d)=
## Applies the inverse of transformation `m` onto `p` in place. ## Applies the inverse of transformation `m` onto `p` in place.
## If the matrix is not invertable (determinant=0) , EDivByZero will ## If the matrix is not invertable (determinant=0) , EDivByZero will
## be raised. ## be raised.
@ -903,48 +903,48 @@ proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
p&=inverse(m) p&=inverse(m)
proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} = proc `+`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
## Adds a vector `v` to a point `p`, resulting ## Adds a vector `v` to a point `p`, resulting
## in a new point. ## in a new point.
result.x=p.x+v.x result.x=p.x+v.x
result.y=p.y+v.y result.y=p.y+v.y
result.z=p.z+v.z result.z=p.z+v.z
proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} = proc `+=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
## Adds a vector `v` to a point `p` in place. ## Adds a vector `v` to a point `p` in place.
p.x+=v.x p.x+=v.x
p.y+=v.y p.y+=v.y
p.z+=v.z p.z+=v.z
proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} = proc `-`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
## Subtracts a vector `v` from a point `p`, resulting ## Subtracts a vector `v` from a point `p`, resulting
## in a new point. ## in a new point.
result.x=p.x-v.x result.x=p.x-v.x
result.y=p.y-v.y result.y=p.y-v.y
result.z=p.z-v.z result.z=p.z-v.z
proc `-`*(p1,p2:TPoint3d):TVector3d {.noInit,inline.} = proc `-`*(p1,p2:Point3d):Vector3d {.noInit,inline.} =
## Subtracts `p2`from `p1` resulting in a difference vector. ## Subtracts `p2`from `p1` resulting in a difference vector.
result.x=p1.x-p2.x result.x=p1.x-p2.x
result.y=p1.y-p2.y result.y=p1.y-p2.y
result.z=p1.z-p2.z result.z=p1.z-p2.z
proc `-=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} = proc `-=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
## Subtracts a vector `v` from a point `p` in place. ## Subtracts a vector `v` from a point `p` in place.
p.x-=v.x p.x-=v.x
p.y-=v.y p.y-=v.y
p.z-=v.z p.z-=v.z
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}= proc equals(p1,p2:Point3d,tol=1.0e-6):bool {.inline.}=
## Checks if two points approximately equals with a tolerance. ## Checks if two points approximately equals with a tolerance.
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
proc `=~`*(p1,p2:TPoint3d):bool {.inline.}= proc `=~`*(p1,p2:Point3d):bool {.inline.}=
## Checks if two vectors approximately equals with a ## Checks if two vectors approximately equals with a
## hardcoded tolerance 1e-6 ## hardcoded tolerance 1e-6
equals(p1,p2) equals(p1,p2)
proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)= proc rotate*(p:var Point3d,rad:float,axis:Vector3d)=
## Rotates point `p` in place `rad` radians about an axis ## Rotates point `p` in place `rad` radians about an axis
## passing through origo. ## passing through origo.
@ -954,7 +954,7 @@ proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
p.y=v.y p.y=v.y
p.z=v.z p.z=v.z
proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)= proc rotate*(p:var Point3d,angle:float,org:Point3d,axis:Vector3d)=
## Rotates point `p` in place `rad` radians about an axis ## Rotates point `p` in place `rad` radians about an axis
## passing through `org` ## passing through `org`
@ -992,26 +992,26 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
proc scale*(p:var TPoint3d,fac:float) {.inline.}= proc scale*(p:var Point3d,fac:float) {.inline.}=
## Scales a point in place `fac` times with world origo as origin. ## Scales a point in place `fac` times with world origo as origin.
p.x*=fac p.x*=fac
p.y*=fac p.y*=fac
p.z*=fac p.z*=fac
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}= proc scale*(p:var Point3d,fac:float,org:Point3d){.inline.}=
## Scales the point in place `fac` times with `org` as origin. ## Scales the point in place `fac` times with `org` as origin.
p.x=(p.x - org.x) * fac + org.x p.x=(p.x - org.x) * fac + org.x
p.y=(p.y - org.y) * fac + org.y p.y=(p.y - org.y) * fac + org.y
p.z=(p.z - org.z) * fac + org.z p.z=(p.z - org.z) * fac + org.z
proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}= proc stretch*(p:var Point3d,facx,facy,facz:float){.inline.}=
## Scales a point in place non uniformly `facx` , `facy` , `facz` times ## Scales a point in place non uniformly `facx` , `facy` , `facz` times
## with world origo as origin. ## with world origo as origin.
p.x*=facx p.x*=facx
p.y*=facy p.y*=facy
p.z*=facz p.z*=facz
proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}= proc stretch*(p:var Point3d,facx,facy,facz:float,org:Point3d){.inline.}=
## Scales the point in place non uniformly `facx` , `facy` , `facz` times ## Scales the point in place non uniformly `facx` , `facy` , `facz` times
## with `org` as origin. ## with `org` as origin.
p.x=(p.x - org.x) * facx + org.x p.x=(p.x - org.x) * facx + org.x
@ -1019,19 +1019,19 @@ proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
p.z=(p.z - org.z) * facz + org.z p.z=(p.z - org.z) * facz + org.z
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}= proc move*(p:var Point3d,dx,dy,dz:float){.inline.}=
## Translates a point `dx` , `dy` , `dz` in place. ## Translates a point `dx` , `dy` , `dz` in place.
p.x+=dx p.x+=dx
p.y+=dy p.y+=dy
p.z+=dz p.z+=dz
proc move*(p:var TPoint3d,v:TVector3d){.inline.}= proc move*(p:var Point3d,v:Vector3d){.inline.}=
## Translates a point with vector `v` in place. ## Translates a point with vector `v` in place.
p.x+=v.x p.x+=v.x
p.y+=v.y p.y+=v.y
p.z+=v.z p.z+=v.z
proc area*(a,b,c:TPoint3d):float {.inline.}= proc area*(a,b,c:Point3d):float {.inline.}=
## Computes the area of the triangle thru points `a` , `b` and `c` ## Computes the area of the triangle thru points `a` , `b` and `c`
# The area of a planar 3d quadliteral is the magnitude of the cross # The area of a planar 3d quadliteral is the magnitude of the cross

View file

@ -46,24 +46,25 @@ const
when sizeof(int) == 4: # 32bit when sizeof(int) == 4: # 32bit
type type
TRaw = range[0..1073741823] Raw = range[0..1073741823]
## The range of uint values that can be stored directly in a value slot ## The range of uint values that can be stored directly in a value slot
## when on a 32 bit platform ## when on a 32 bit platform
{.deprecated: [TRaw: Raw].}
elif sizeof(int) == 8: # 64bit elif sizeof(int) == 8: # 64bit
type type
TRaw = range[0..4611686018427387903] Raw = range[0..4611686018427387903]
## The range of uint values that can be stored directly in a value slot ## The range of uint values that can be stored directly in a value slot
## when on a 64 bit platform ## when on a 64 bit platform
{.deprecated: [TRaw: Raw].}
else: else:
{.error: "unsupported platform".} {.error: "unsupported platform".}
type type
TEntry = tuple Entry = tuple
key: int key: int
value: int value: int
TEntryArr = ptr array[0..10_000_000, TEntry] EntryArr = ptr array[0..10_000_000, Entry]
PConcTable[K,V] = ptr object {.pure.} PConcTable[K,V] = ptr object {.pure.}
len: int len: int
@ -72,8 +73,8 @@ type
copyIdx: int copyIdx: int
copyDone: int copyDone: int
next: PConcTable[K,V] next: PConcTable[K,V]
data: TEntryArr data: EntryArr
{.deprecated: [TEntry: Entry, TEntryArr: EntryArr.}
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
expVal: int, match: bool): int expVal: int, match: bool): int
@ -84,7 +85,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] = proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
let let
dataLen = max(nextPowerOfTwo(size), minTableSize) dataLen = max(nextPowerOfTwo(size), minTableSize)
dataSize = dataLen*sizeof(TEntry) dataSize = dataLen*sizeof(Entry)
dataMem = allocShared0(dataSize) dataMem = allocShared0(dataSize)
tableSize = 7 * intSize tableSize = 7 * intSize
tableMem = allocShared0(tableSize) tableMem = allocShared0(tableSize)
@ -95,7 +96,7 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
table.copyIdx = 0 table.copyIdx = 0
table.copyDone = 0 table.copyDone = 0
table.next = nil table.next = nil
table.data = cast[TEntryArr](dataMem) table.data = cast[EntryArr](dataMem)
result = table result = table
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
@ -107,7 +108,7 @@ proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var TEntry {.inline.} = proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
table.data[i] table.data[i]
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
@ -191,7 +192,7 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} = #proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
proc keyEQ[K](key1: int, key2: int): bool {.inline.} = proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
result = false result = false
when K is TRaw: when K is Raw:
if key1 == key2: if key1 == key2:
result = true result = true
else: else:
@ -236,7 +237,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
break break
#echo("oldVal was = ", oldVal, " set it to prime ", box) #echo("oldVal was = ", oldVal, " set it to prime ", box)
if isPrime(oldVal) and isTomb(oldVal): if isPrime(oldVal) and isTomb(oldVal):
#when not (K is TRaw): #when not (K is Raw):
# deallocShared(popPtr[K](oldKey)) # deallocShared(popPtr[K](oldKey))
return false return false
if isTomb(oldVal): if isTomb(oldVal):
@ -343,7 +344,7 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
expVal: int, match: bool): int = expVal: int, match: bool): int =
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val) #echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
when K is TRaw: when K is Raw:
var idx = hashInt(key) var idx = hashInt(key)
else: else:
var idx = popPtr[K](key)[].hash var idx = popPtr[K](key)[].hash
@ -428,7 +429,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
proc getVal[K,V](table: var PConcTable[K,V], key: int): int = proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
#echo("-try get- key = " & $key) #echo("-try get- key = " & $key)
when K is TRaw: when K is Raw:
var idx = hashInt(key) var idx = hashInt(key)
else: else:
var idx = popPtr[K](key)[].hash var idx = popPtr[K](key)[].hash
@ -468,37 +469,37 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) = #proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
# discard setVal(table, pack(key), pack(key), 0, false) # discard setVal(table, pack(key), pack(key), 0, false)
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) = #proc set*[V](table: var PConcTable[Raw,V], key: Raw, val: ptr V) =
# discard setVal(table, pack(key), cast[int](val), 0, false) # discard setVal(table, pack(key), cast[int](val), 0, false)
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) = proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
when not (K is TRaw): when not (K is Raw):
var newKey = cast[int](copyShared(key)) var newKey = cast[int](copyShared(key))
else: else:
var newKey = pack(key) var newKey = pack(key)
when not (V is TRaw): when not (V is Raw):
var newVal = cast[int](copyShared(val)) var newVal = cast[int](copyShared(val))
else: else:
var newVal = pack(val) var newVal = pack(val)
var oldPtr = pop(setVal(table, newKey, newVal, 0, false)) var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr)) #echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
when not (V is TRaw): when not (V is Raw):
if newVal != oldPtr and oldPtr != 0: if newVal != oldPtr and oldPtr != 0:
deallocShared(cast[ptr V](oldPtr)) deallocShared(cast[ptr V](oldPtr))
proc get*[K,V](table: var PConcTable[K,V], key: var K): V = proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
when not (V is TRaw): when not (V is Raw):
when not (K is TRaw): when not (K is Raw):
return popPtr[V](getVal(table, cast[int](key.addr)))[] return popPtr[V](getVal(table, cast[int](key.addr)))[]
else: else:
return popPtr[V](getVal(table, pack(key)))[] return popPtr[V](getVal(table, pack(key)))[]
else: else:
when not (K is TRaw): when not (K is Raw):
return popRaw(getVal(table, cast[int](key.addr))) return popRaw(getVal(table, cast[int](key.addr)))
else: else:
return popRaw(getVal(table, pack(key))) return popRaw(getVal(table, pack(key)))
@ -535,23 +536,24 @@ when not defined(testing) and isMainModule:
type type
TTestObj = tuple TestObj = tuple
thr: int thr: int
f0: int f0: int
f1: int f1: int
TData = tuple[k: string,v: TTestObj] Data = tuple[k: string,v: TestObj]
PDataArr = array[0..numTests-1, TData] PDataArr = array[0..numTests-1, Data]
Dict = PConcTable[string,TTestObj] Dict = PConcTable[string,TestObj]
{.deprecated: [TTestObj: TestObj, TData: Data].}
var var
thr: array[0..numThreads-1, TThread[Dict]] thr: array[0..numThreads-1, Thread[Dict]]
table = newLFTable[string,TTestObj](8) table = newLFTable[string,TestObj](8)
rand = newMersenneTwister(2525) rand = newMersenneTwister(2525)
proc createSampleData(len: int): PDataArr = proc createSampleData(len: int): PDataArr =
#result = cast[PDataArr](allocShared0(sizeof(TData)*numTests)) #result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
for i in 0..len-1: for i in 0..len-1:
result[i].k = "mark" & $(i+1) result[i].k = "mark" & $(i+1)
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1))) #echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))

View file

@ -30,25 +30,25 @@ const
IntMask = 1 shl IntShift - 1 IntMask = 1 shl IntShift - 1
type type
PTrunk = ref TTrunk PTrunk = ref Trunk
TTrunk {.final.} = object Trunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0 key: int # start address at bit 0
bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
TTrunkSeq = seq[PTrunk] TrunkSeq = seq[PTrunk]
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
counter, max: int counter, max: int
head: PTrunk head: PTrunk
data: TTrunkSeq data: TrunkSeq
{.deprecated: [TIntSet: IntSet].} {.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].}
proc mustRehash(length, counter: int): bool {.inline.} = proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash {.inline.} = proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash result = ((5 * h) + 1) and maxHash
proc intSetGet(t: IntSet, key: int): PTrunk = proc intSetGet(t: IntSet, key: int): PTrunk =
@ -59,7 +59,7 @@ proc intSetGet(t: IntSet, key: int): PTrunk =
h = nextTry(h, t.max) h = nextTry(h, t.max)
result = nil result = nil
proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) = proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
var h = desc.key and t.max var h = desc.key and t.max
while data[h] != nil: while data[h] != nil:
assert(data[h] != desc) assert(data[h] != desc)
@ -68,7 +68,7 @@ proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
data[h] = desc data[h] = desc
proc intSetEnlarge(t: var IntSet) = proc intSetEnlarge(t: var IntSet) =
var n: TTrunkSeq var n: TrunkSeq
var oldMax = t.max var oldMax = t.max
t.max = ((t.max + 1) * 2) - 1 t.max = ((t.max + 1) * 2) - 1
newSeq(n, t.max + 1) newSeq(n, t.max + 1)

View file

@ -29,7 +29,7 @@ when not defined(nimhygiene):
# codes should never be needed, and this can pack more entries per cache-line. # codes should never be needed, and this can pack more entries per cache-line.
# Losing hcode entirely is also possible - if some element value is forbidden. # Losing hcode entirely is also possible - if some element value is forbidden.
type type
KeyValuePair[A] = tuple[hcode: THash, key: A] KeyValuePair[A] = tuple[hcode: Hash, key: A]
KeyValuePairSeq[A] = seq[KeyValuePair[A]] KeyValuePairSeq[A] = seq[KeyValuePair[A]]
HashSet* {.myShallow.}[A] = object ## \ HashSet* {.myShallow.}[A] = object ## \
## A generic hash set. ## A generic hash set.
@ -43,10 +43,10 @@ type
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These # hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
# two procs retain clarity of that encoding without the space cost of an enum. # two procs retain clarity of that encoding without the space cost of an enum.
proc isEmpty(hcode: THash): bool {.inline.} = proc isEmpty(hcode: Hash): bool {.inline.} =
result = hcode == 0 result = hcode == 0
proc isFilled(hcode: THash): bool {.inline.} = proc isFilled(hcode: Hash): bool {.inline.} =
result = hcode != 0 result = hcode != 0
proc isValid*[A](s: HashSet[A]): bool = proc isValid*[A](s: HashSet[A]): bool =
@ -58,7 +58,7 @@ proc isValid*[A](s: HashSet[A]): bool =
## initialized. Example: ## initialized. Example:
## ##
## .. code-block :: ## .. code-block ::
## proc savePreferences(options: TSet[string]) = ## proc savePreferences(options: Set[string]) =
## assert options.isValid, "Pass an initialized set!" ## assert options.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds! ## # Do stuff here, may crash in release builds!
result = not s.data.isNil result = not s.data.isNil
@ -72,7 +72,7 @@ proc len*[A](s: HashSet[A]): int =
## ##
## .. code-block:: ## .. code-block::
## ##
## var values: TSet[int] ## var values: Set[int]
## assert(not values.isValid) ## assert(not values.isValid)
## assert values.len == 0 ## assert values.len == 0
result = s.counter result = s.counter
@ -123,15 +123,15 @@ proc rightSize*(count: Natural): int {.inline.} =
## Internally, we want mustRehash(rightSize(x), x) == false. ## Internally, we want mustRehash(rightSize(x), x) == false.
result = nextPowerOfTwo(count * 3 div 2 + 4) result = nextPowerOfTwo(count * 3 div 2 + 4)
proc nextTry(h, maxHash: THash): THash {.inline.} = proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash result = (h + 1) and maxHash
template rawGetKnownHCImpl() {.dirty.} = template rawGetKnownHCImpl() {.dirty.} =
var h: THash = hc and high(s.data) # start with real hash value var h: Hash = hc and high(s.data) # start with real hash value
while isFilled(s.data[h].hcode): while isFilled(s.data[h].hcode):
# Compare hc THEN key with boolean short circuit. This makes the common case # Compare hc THEN key with boolean short circuit. This makes the common case
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present. # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
# It does slow down succeeding lookups by one extra THash cmp&and..usually # It does slow down succeeding lookups by one extra Hash cmp&and..usually
# just a few clock cycles, generally worth it for any non-integer-like A. # just a few clock cycles, generally worth it for any non-integer-like A.
if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key
return h return h
@ -148,10 +148,10 @@ template rawInsertImpl() {.dirty.} =
data[h].key = key data[h].key = key
data[h].hcode = hc data[h].hcode = hc
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: THash): int {.inline.} = proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl() rawGetKnownHCImpl()
proc rawGet[A](s: HashSet[A], key: A, hc: var THash): int {.inline.} = proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
rawGetImpl() rawGetImpl()
proc mget*[A](s: var HashSet[A], key: A): var A = proc mget*[A](s: var HashSet[A], key: A): var A =
@ -160,7 +160,7 @@ proc mget*[A](s: var HashSet[A], key: A): var A =
## when one overloaded 'hash' and '==' but still needs reference semantics ## when one overloaded 'hash' and '==' but still needs reference semantics
## for sharing. ## for sharing.
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var hc: THash var hc: Hash
var index = rawGet(s, key, hc) var index = rawGet(s, key, hc)
if index >= 0: result = s.data[index].key if index >= 0: result = s.data[index].key
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
@ -178,12 +178,12 @@ proc contains*[A](s: HashSet[A], key: A): bool =
## values.excl(2) ## values.excl(2)
## assert(not values.contains(2)) ## assert(not values.contains(2))
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var hc: THash var hc: Hash
var index = rawGet(s, key, hc) var index = rawGet(s, key, hc)
result = index >= 0 result = index >= 0
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A, proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
hc: THash, h: THash) = hc: Hash, h: Hash) =
rawInsertImpl() rawInsertImpl()
proc enlarge[A](s: var HashSet[A]) = proc enlarge[A](s: var HashSet[A]) =
@ -196,7 +196,7 @@ proc enlarge[A](s: var HashSet[A]) =
rawInsert(s, s.data, n[i].key, n[i].hcode, j) rawInsert(s, s.data, n[i].key, n[i].hcode, j)
template inclImpl() {.dirty.} = template inclImpl() {.dirty.} =
var hc: THash var hc: Hash
var index = rawGet(s, key, hc) var index = rawGet(s, key, hc)
if index < 0: if index < 0:
if mustRehash(len(s.data), s.counter): if mustRehash(len(s.data), s.counter):
@ -206,7 +206,7 @@ template inclImpl() {.dirty.} =
inc(s.counter) inc(s.counter)
template containsOrInclImpl() {.dirty.} = template containsOrInclImpl() {.dirty.} =
var hc: THash var hc: Hash
var index = rawGet(s, key, hc) var index = rawGet(s, key, hc)
if index >= 0: if index >= 0:
result = true result = true
@ -261,7 +261,7 @@ proc excl*[A](s: var HashSet[A], key: A) =
## s.excl(2) ## s.excl(2)
## assert s.len == 3 ## assert s.len == 3
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var hc: THash var hc: Hash
var i = rawGet(s, key, hc) var i = rawGet(s, key, hc)
var msk = high(s.data) var msk = high(s.data)
if i >= 0: if i >= 0:
@ -323,7 +323,7 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example: ## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
## ##
## .. code-block :: ## .. code-block ::
## var a: TSet[int] ## var a: Set[int]
## a.init(4) ## a.init(4)
## a.incl(2) ## a.incl(2)
## a.init ## a.init
@ -552,7 +552,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
type type
OrderedKeyValuePair[A] = tuple[ OrderedKeyValuePair[A] = tuple[
hcode: THash, next: int, key: A] hcode: Hash, next: int, key: A]
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]] OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
OrderedSet* {.myShallow.}[A] = object ## \ OrderedSet* {.myShallow.}[A] = object ## \
## A generic hash set that remembers insertion order. ## A generic hash set that remembers insertion order.
@ -574,7 +574,7 @@ proc isValid*[A](s: OrderedSet[A]): bool =
## correctly initialized. Example: ## correctly initialized. Example:
## ##
## .. code-block:: ## .. code-block::
## proc saveTarotCards(cards: TOrderedSet[int]) = ## proc saveTarotCards(cards: OrderedSet[int]) =
## assert cards.isValid, "Pass an initialized set!" ## assert cards.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds! ## # Do stuff here, may crash in release builds!
result = not s.data.isNil result = not s.data.isNil
@ -588,7 +588,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
## ##
## .. code-block:: ## .. code-block::
## ##
## var values: TOrderedSet[int] ## var values: OrderedSet[int]
## assert(not values.isValid) ## assert(not values.isValid)
## assert values.len == 0 ## assert values.len == 0
result = s.counter result = s.counter
@ -629,10 +629,10 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs: forAllOrderedPairs:
yield s.data[h].key yield s.data[h].key
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: THash): int {.inline.} = proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl() rawGetKnownHCImpl()
proc rawGet[A](s: OrderedSet[A], key: A, hc: var THash): int {.inline.} = proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
rawGetImpl() rawGetImpl()
proc contains*[A](s: OrderedSet[A], key: A): bool = proc contains*[A](s: OrderedSet[A], key: A): bool =
@ -646,12 +646,12 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
## values.incl(2) ## values.incl(2)
## assert values.contains(2) ## assert values.contains(2)
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var hc: THash var hc: Hash
var index = rawGet(s, key, hc) var index = rawGet(s, key, hc)
result = index >= 0 result = index >= 0
proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A], proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
key: A, hc: THash, h: THash) = key: A, hc: Hash, h: Hash) =
rawInsertImpl() rawInsertImpl()
data[h].next = -1 data[h].next = -1
if s.first < 0: s.first = h if s.first < 0: s.first = h
@ -729,7 +729,7 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
## from an ordered hash set. Example: ## from an ordered hash set. Example:
## ##
## .. code-block :: ## .. code-block ::
## var a: TOrderedSet[int] ## var a: OrderedSet[int]
## a.init(4) ## a.init(4)
## a.incl(2) ## a.incl(2)
## a.init ## a.init

View file

@ -24,13 +24,13 @@
## ##
## Error: type mismatch: got (Person) ## Error: type mismatch: got (Person)
## but expected one of: ## but expected one of:
## hashes.hash(x: openarray[A]): THash ## hashes.hash(x: openarray[A]): Hash
## hashes.hash(x: int): THash ## hashes.hash(x: int): Hash
## hashes.hash(x: float): THash ## hashes.hash(x: float): Hash
## … ## …
## ##
## What is happening here is that the types used for table keys require to have ## What is happening here is that the types used for table keys require to have
## a ``hash()`` proc which will convert them to a `THash <hashes.html#THash>`_ ## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_
## value, and the compiler is listing all the hash functions it knows. ## value, and the compiler is listing all the hash functions it knows.
## Additionally there has to be a ``==`` operator that provides the same ## Additionally there has to be a ``==`` operator that provides the same
## semantics as its corresponding ``hash`` proc. ## semantics as its corresponding ``hash`` proc.
@ -46,7 +46,7 @@
## Person = object ## Person = object
## firstName, lastName: string ## firstName, lastName: string
## ##
## proc hash(x: Person): THash = ## proc hash(x: Person): Hash =
## ## Piggyback on the already available string hash proc. ## ## Piggyback on the already available string hash proc.
## ## ## ##
## ## Without this proc nothing works! ## ## Without this proc nothing works!
@ -71,7 +71,7 @@ import
{.pragma: myShallow.} {.pragma: myShallow.}
type type
KeyValuePair[A, B] = tuple[hcode: THash, key: A, val: B] KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]] KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
Table* {.myShallow.}[A, B] = object ## generic hash table Table* {.myShallow.}[A, B] = object ## generic hash table
data: KeyValuePairSeq[A, B] data: KeyValuePairSeq[A, B]
@ -85,10 +85,10 @@ when not defined(nimhygiene):
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These # hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
# two procs retain clarity of that encoding without the space cost of an enum. # two procs retain clarity of that encoding without the space cost of an enum.
proc isEmpty(hcode: THash): bool {.inline.} = proc isEmpty(hcode: Hash): bool {.inline.} =
result = hcode == 0 result = hcode == 0
proc isFilled(hcode: THash): bool {.inline.} = proc isFilled(hcode: Hash): bool {.inline.} =
result = hcode != 0 result = hcode != 0
proc len*[A, B](t: Table[A, B]): int = proc len*[A, B](t: Table[A, B]): int =
@ -137,15 +137,15 @@ proc rightSize*(count: Natural): int {.inline.} =
## Internally, we want mustRehash(rightSize(x), x) == false. ## Internally, we want mustRehash(rightSize(x), x) == false.
result = nextPowerOfTwo(count * 3 div 2 + 4) result = nextPowerOfTwo(count * 3 div 2 + 4)
proc nextTry(h, maxHash: THash): THash {.inline.} = proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash result = (h + 1) and maxHash
template rawGetKnownHCImpl() {.dirty.} = template rawGetKnownHCImpl() {.dirty.} =
var h: THash = hc and high(t.data) # start with real hash value var h: Hash = hc and high(t.data) # start with real hash value
while isFilled(t.data[h].hcode): while isFilled(t.data[h].hcode):
# Compare hc THEN key with boolean short circuit. This makes the common case # Compare hc THEN key with boolean short circuit. This makes the common case
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present. # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
# It does slow down succeeding lookups by one extra THash cmp&and..usually # It does slow down succeeding lookups by one extra Hash cmp&and..usually
# just a few clock cycles, generally worth it for any non-integer-like A. # just a few clock cycles, generally worth it for any non-integer-like A.
if t.data[h].hcode == hc and t.data[h].key == key: if t.data[h].hcode == hc and t.data[h].key == key:
return h return h
@ -162,7 +162,7 @@ template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
hc = hash(key) hc = hash(key)
if hc == 0: if hc == 0:
hc = 314159265 hc = 314159265
var h: THash = hc and high(t.data) var h: Hash = hc and high(t.data)
while isFilled(t.data[h].hcode): while isFilled(t.data[h].hcode):
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = h result = h
@ -172,13 +172,13 @@ template rawInsertImpl() {.dirty.} =
data[h].val = val data[h].val = val
data[h].hcode = hc data[h].hcode = hc
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: THash): int {.inline.} = proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl() rawGetKnownHCImpl()
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} = proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
rawGetDeepImpl() rawGetDeepImpl()
proc rawGet[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} = proc rawGet[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
rawGetImpl() rawGetImpl()
proc `[]`*[A, B](t: Table[A, B], key: A): B = proc `[]`*[A, B](t: Table[A, B], key: A): B =
@ -186,14 +186,14 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B =
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
## exists. ## exists.
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var Table[A, B], key: A): var B = proc mget*[A, B](t: var Table[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised. ## If `key` is not in `t`, the ``KeyError`` exception is raised.
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: else:
@ -204,7 +204,7 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
iterator allValues*[A, B](t: Table[A, B]; key: A): B = iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`. ## iterates over any value in the table `t` that belongs to the given `key`.
var h: THash = hash(key) and high(t.data) var h: Hash = hash(key) and high(t.data)
while isFilled(t.data[h].hcode): while isFilled(t.data[h].hcode):
if t.data[h].key == key: if t.data[h].key == key:
yield t.data[h].val yield t.data[h].val
@ -212,7 +212,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
proc hasKey*[A, B](t: Table[A, B], key: A): bool = proc hasKey*[A, B](t: Table[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
var hc: THash var hc: Hash
result = rawGet(t, key, hc) >= 0 result = rawGet(t, key, hc) >= 0
proc contains*[A, B](t: Table[A, B], key: A): bool = proc contains*[A, B](t: Table[A, B], key: A): bool =
@ -220,7 +220,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
return hasKey[A, B](t, key) return hasKey[A, B](t, key)
proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B], proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B],
key: A, val: B, hc: THash, h: THash) = key: A, val: B, hc: Hash, h: Hash) =
rawInsertImpl() rawInsertImpl()
proc enlarge[A, B](t: var Table[A, B]) = proc enlarge[A, B](t: var Table[A, B]) =
@ -234,7 +234,7 @@ proc enlarge[A, B](t: var Table[A, B]) =
template addImpl() {.dirty.} = template addImpl() {.dirty.} =
if mustRehash(len(t.data), t.counter): enlarge(t) if mustRehash(len(t.data), t.counter): enlarge(t)
var hc: THash var hc: Hash
var j = rawGetDeep(t, key, hc) var j = rawGetDeep(t, key, hc)
rawInsert(t, t.data, key, val, hc, j) rawInsert(t, t.data, key, val, hc, j)
inc(t.counter) inc(t.counter)
@ -248,19 +248,19 @@ template maybeRehashPutImpl() {.dirty.} =
inc(t.counter) inc(t.counter)
template putImpl() {.dirty.} = template putImpl() {.dirty.} =
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: t.data[index].val = val if index >= 0: t.data[index].val = val
else: maybeRehashPutImpl() else: maybeRehashPutImpl()
template mgetOrPutImpl() {.dirty.} = template mgetOrPutImpl() {.dirty.} =
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index < 0: maybeRehashPutImpl() # not present: insert (flipping index) if index < 0: maybeRehashPutImpl() # not present: insert (flipping index)
result = t.data[index].val # either way return modifiable val result = t.data[index].val # either way return modifiable val
template hasKeyOrPutImpl() {.dirty.} = template hasKeyOrPutImpl() {.dirty.} =
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index < 0: if index < 0:
result = false result = false
@ -291,7 +291,7 @@ template doWhile(a: expr, b: stmt): stmt =
proc del*[A, B](t: var Table[A, B], key: A) = proc del*[A, B](t: var Table[A, B], key: A) =
## deletes `key` from hash table `t`. ## deletes `key` from hash table `t`.
var hc: THash var hc: Hash
var i = rawGet(t, key, hc) var i = rawGet(t, key, hc)
let msk = high(t.data) let msk = high(t.data)
if i >= 0: if i >= 0:
@ -460,7 +460,7 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B]
type type
OrderedKeyValuePair[A, B] = tuple[ OrderedKeyValuePair[A, B] = tuple[
hcode: THash, next: int, key: A, val: B] hcode: Hash, next: int, key: A, val: B]
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]] OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
OrderedTable* {. OrderedTable* {.
myShallow.}[A, B] = object ## table that remembers insertion order myShallow.}[A, B] = object ## table that remembers insertion order
@ -509,13 +509,13 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].val yield t.data[h].val
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: THash): int = proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
rawGetKnownHCImpl() rawGetKnownHCImpl()
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int {.inline.} = proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
rawGetDeepImpl() rawGetDeepImpl()
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int = proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
rawGetImpl() rawGetImpl()
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
@ -523,21 +523,21 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
## exists. ## exists.
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B = proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var hc: THash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
var hc: THash var hc: Hash
result = rawGet(t, key, hc) >= 0 result = rawGet(t, key, hc) >= 0
proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
@ -546,7 +546,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
proc rawInsert[A, B](t: var OrderedTable[A, B], proc rawInsert[A, B](t: var OrderedTable[A, B],
data: var OrderedKeyValuePairSeq[A, B], data: var OrderedKeyValuePairSeq[A, B],
key: A, val: B, hc: THash, h: THash) = key: A, val: B, hc: Hash, h: Hash) =
rawInsertImpl() rawInsertImpl()
data[h].next = -1 data[h].next = -1
if t.first < 0: t.first = h if t.first < 0: t.first = h
@ -796,7 +796,7 @@ iterator mvalues*[A](t: CountTable[A]): var int =
if t.data[h].val != 0: yield t.data[h].val if t.data[h].val != 0: yield t.data[h].val
proc rawGet[A](t: CountTable[A], key: A): int = proc rawGet[A](t: CountTable[A], key: A): int =
var h: THash = hash(key) and high(t.data) # start with real hash value var h: Hash = hash(key) and high(t.data) # start with real hash value
while t.data[h].val != 0: while t.data[h].val != 0:
if t.data[h].key == key: return h if t.data[h].key == key: return h
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
@ -826,7 +826,7 @@ proc contains*[A](t: CountTable[A], key: A): bool =
proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]], proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
key: A, val: int) = key: A, val: int) =
var h: THash = hash(key) and high(data) var h: Hash = hash(key) and high(data)
while data[h].val != 0: h = nextTry(h, high(data)) while data[h].val != 0: h = nextTry(h, high(data))
data[h].key = key data[h].key = key
data[h].val = val data[h].val = val
@ -1032,7 +1032,7 @@ when isMainModule:
Person = object Person = object
firstName, lastName: string firstName, lastName: string
proc hash(x: Person): THash = proc hash(x: Person): Hash =
## Piggyback on the already available string hash proc. ## Piggyback on the already available string hash proc.
## ##
## Without this proc nothing works! ## Without this proc nothing works!

View file

@ -34,7 +34,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
sysIdle, sysKernel, sysUser, sysIdle, sysKernel, sysUser,
procCreation, procExit, procKernel, procUser: TFILETIME procCreation, procExit, procKernel, procUser: TFILETIME
if getSystemTimes(sysIdle, sysKernel, sysUser) == 0 or if getSystemTimes(sysIdle, sysKernel, sysUser) == 0 or
getProcessTimes(THandle(-1), procCreation, procExit, getProcessTimes(Handle(-1), procCreation, procExit,
procKernel, procUser) == 0: procKernel, procUser) == 0:
return doNothing return doNothing
if s.calls > 0: if s.calls > 0:

View file

@ -18,8 +18,8 @@ import cpuinfo, cpuload, locks
type type
Semaphore = object Semaphore = object
c: TCond c: Cond
L: TLock L: Lock
counter: int counter: int
proc createSemaphore(): Semaphore = proc createSemaphore(): Semaphore =
@ -113,7 +113,7 @@ type
ToFreeQueue = object ToFreeQueue = object
len: int len: int
lock: TLock lock: Lock
empty: Semaphore empty: Semaphore
data: array[128, pointer] data: array[128, pointer]
@ -221,11 +221,17 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
action(fv.blob) action(fv.blob)
finished(fv) finished(fv)
proc `^`*[T](fv: FlowVar[ref T]): foreign ptr T = proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
## blocks until the value is available and then returns this value. ## blocks until the value is available and then returns this value.
await(fv) await(fv)
result = cast[foreign ptr T](fv.data) result = cast[foreign ptr T](fv.data)
proc `^`*[T](fv: FlowVar[ref T]): ref T =
## blocks until the value is available and then returns this value.
await(fv)
let src = cast[ref T](fv.data)
deepCopy result, src
proc `^`*[T](fv: FlowVar[T]): T = proc `^`*[T](fv: FlowVar[T]): T =
## blocks until the value is available and then returns this value. ## blocks until the value is available and then returns this value.
await(fv) await(fv)
@ -349,7 +355,7 @@ proc parallel*(body: stmt) {.magic: "Parallel".}
var var
state: ThreadPoolState state: ThreadPoolState
stateLock: TLock stateLock: Lock
initLock stateLock initLock stateLock

View file

@ -211,12 +211,13 @@ when defined(windows):
when false: when false:
# not needed yet: # not needed yet:
type type
TCpInfo = object CpInfo = object
maxCharSize: int32 maxCharSize: int32
defaultChar: array[0..1, char] defaultChar: array[0..1, char]
leadByte: array[0..12-1, char] leadByte: array[0..12-1, char]
{.deprecated: [TCpInfo: CpInfo].}
proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {. proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
stdcall, importc: "GetCPInfo", dynlib: "kernel32".} stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
proc nameToCodePage(name: string): CodePage = proc nameToCodePage(name: string): CodePage =

23
lib/pure/etcpriv.nim Normal file
View file

@ -0,0 +1,23 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Nim Authors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains utils that are less then easy to categorize and
## don't really warrant a specific module. They are private to compiler
## and stdlib usage, and should not be used outside of that - they may
## change or disappear at any time.
# Used by pure/hashes.nim, and the compiler parsing
const magicIdentSeparatorRuneByteWidth* = 3
# Used by pure/hashes.nim, and the compiler parsing
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } =
result = cs[i] == '\226' and
cs[i + 1] == '\128' and
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen

View file

@ -18,20 +18,22 @@ import
os, hashes, strutils os, hashes, strutils
type type
TGenTableMode* = enum ## describes the table's key matching mode GenTableMode* = enum ## describes the table's key matching mode
modeCaseSensitive, ## case sensitive matching of keys modeCaseSensitive, ## case sensitive matching of keys
modeCaseInsensitive, ## case insensitive matching of keys modeCaseInsensitive, ## case insensitive matching of keys
modeStyleInsensitive ## style sensitive matching of keys modeStyleInsensitive ## style sensitive matching of keys
TGenKeyValuePair[T] = tuple[key: string, val: T] GenKeyValuePair[T] = tuple[key: string, val: T]
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]] GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
TGenTable*[T] = object of RootObj GenTable*[T] = object of RootObj
counter: int counter: int
data: TGenKeyValuePairSeq[T] data: GenKeyValuePairSeq[T]
mode: TGenTableMode mode: GenTableMode
PGenTable*[T] = ref TGenTable[T] ## use this type to declare hash tables PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
const const
growthFactor = 2 growthFactor = 2
@ -48,7 +50,7 @@ iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
if not isNil(tbl.data[h].key): if not isNil(tbl.data[h].key):
yield (tbl.data[h].key, tbl.data[h].val) yield (tbl.data[h].key, tbl.data[h].val)
proc myhash[T](tbl: PGenTable[T], key: string): THash = proc myhash[T](tbl: PGenTable[T], key: string): Hash =
case tbl.mode case tbl.mode
of modeCaseSensitive: result = hashes.hash(key) of modeCaseSensitive: result = hashes.hash(key)
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key) of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
@ -64,18 +66,18 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc newGenTable*[T](mode: TGenTableMode): PGenTable[T] = proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
## creates a new generic hash table that is empty. ## creates a new generic hash table that is empty.
new(result) new(result)
result.mode = mode result.mode = mode
result.counter = 0 result.counter = 0
newSeq(result.data, startSize) newSeq(result.data, startSize)
proc nextTry(h, maxHash: THash): THash {.inline.} = proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash result = ((5 * h) + 1) and maxHash
proc rawGet[T](tbl: PGenTable[T], key: string): int = proc rawGet[T](tbl: PGenTable[T], key: string): int =
var h: THash var h: Hash
h = myhash(tbl, key) and high(tbl.data) # start with real hash value h = myhash(tbl, key) and high(tbl.data) # start with real hash value
while not isNil(tbl.data[h].key): while not isNil(tbl.data[h].key):
if myCmp(tbl, tbl.data[h].key, key): if myCmp(tbl, tbl.data[h].key, key):
@ -83,9 +85,9 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
h = nextTry(h, high(tbl.data)) h = nextTry(h, high(tbl.data))
result = - 1 result = - 1
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T], proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
key: string, val: T) = key: string, val: T) =
var h: THash var h: Hash
h = myhash(tbl, key) and high(data) h = myhash(tbl, key) and high(data)
while not isNil(data[h].key): while not isNil(data[h].key):
h = nextTry(h, high(data)) h = nextTry(h, high(data))
@ -93,7 +95,7 @@ proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
data[h].val = val data[h].val = val
proc enlarge[T](tbl: PGenTable[T]) = proc enlarge[T](tbl: PGenTable[T]) =
var n: TGenKeyValuePairSeq[T] var n: GenKeyValuePairSeq[T]
newSeq(n, len(tbl.data) * growthFactor) newSeq(n, len(tbl.data) * growthFactor)
for i in countup(0, high(tbl.data)): for i in countup(0, high(tbl.data)):
if not isNil(tbl.data[i].key): if not isNil(tbl.data[i].key):
@ -146,19 +148,20 @@ when isMainModule:
# Verify a table of user-defined types # Verify a table of user-defined types
# #
type type
TMyType = tuple[first, second: string] # a pair of strings MyType = tuple[first, second: string] # a pair of strings
{.deprecated: [TMyType: MyType].}
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
# value is TMyType tuple # value is MyType tuple
#var junk: TMyType = ("OK", "Here") #var junk: MyType = ("OK", "Here")
#echo junk.first, " ", junk.second #echo junk.first, " ", junk.second
y["Hello"] = ("Hello", "World") y["Hello"] = ("Hello", "World")
y["Goodbye"] = ("Goodbye", "Everyone") y["Goodbye"] = ("Goodbye", "Everyone")
#y["Hello"] = TMyType( ("Hello", "World") ) #y["Hello"] = MyType( ("Hello", "World") )
#y["Goodbye"] = TMyType( ("Goodbye", "Everyone") ) #y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
assert( not isNil(y["Hello"].first) ) assert( not isNil(y["Hello"].first) )
assert( y["Hello"].first == "Hello" ) assert( y["Hello"].first == "Hello" )

View file

@ -15,9 +15,9 @@
## code: ## code:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## proc hash(x: Something): THash = ## proc hash(x: Something): Hash =
## ## Computes a THash from `x`. ## ## Computes a Hash from `x`.
## var h: THash = 0 ## var h: Hash = 0
## # Iterate over parts of `x`. ## # Iterate over parts of `x`.
## for xAtom in x: ## for xAtom in x:
## # Mix the atom with the partial hash. ## # Mix the atom with the partial hash.
@ -30,38 +30,39 @@
## together the hash value of the individual fields: ## together the hash value of the individual fields:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## proc hash(x: Something): THash = ## proc hash(x: Something): Hash =
## ## Computes a THash from `x`. ## ## Computes a Hash from `x`.
## var h: THash = 0 ## var h: Hash = 0
## h = h !& hash(x.foo) ## h = h !& hash(x.foo)
## h = h !& hash(x.bar) ## h = h !& hash(x.bar)
## result = !$h ## result = !$h
import import
strutils strutils, etcpriv
type type
THash* = int ## a hash value; hash tables using these values should Hash* = int ## a hash value; hash tables using these values should
## always have a size of a power of two and can use the ``and`` ## always have a size of a power of two and can use the ``and``
## operator instead of ``mod`` for truncation of the hash value. ## operator instead of ``mod`` for truncation of the hash value.
{.deprecated: [THash: Hash].}
proc `!&`*(h: THash, val: int): THash {.inline.} = proc `!&`*(h: Hash, val: int): Hash {.inline.} =
## mixes a hash value `h` with `val` to produce a new hash value. This is ## mixes a hash value `h` with `val` to produce a new hash value. This is
## only needed if you need to implement a hash proc for a new datatype. ## only needed if you need to implement a hash proc for a new datatype.
result = h +% val result = h +% val
result = result +% result shl 10 result = result +% result shl 10
result = result xor (result shr 6) result = result xor (result shr 6)
proc `!$`*(h: THash): THash {.inline.} = proc `!$`*(h: Hash): Hash {.inline.} =
## finishes the computation of the hash value. This is ## finishes the computation of the hash value. This is
## only needed if you need to implement a hash proc for a new datatype. ## only needed if you need to implement a hash proc for a new datatype.
result = h +% h shl 3 result = h +% h shl 3
result = result xor (result shr 11) result = result xor (result shr 11)
result = result +% result shl 15 result = result +% result shl 15
proc hashData*(data: pointer, size: int): THash = proc hashData*(data: pointer, size: int): Hash =
## hashes an array of bytes of size `size` ## hashes an array of bytes of size `size`
var h: THash = 0 var h: Hash = 0
when defined(js): when defined(js):
var p: cstring var p: cstring
asm """`p` = `Data`;""" asm """`p` = `Data`;"""
@ -78,7 +79,7 @@ proc hashData*(data: pointer, size: int): THash =
when defined(js): when defined(js):
var objectID = 0 var objectID = 0
proc hash*(x: pointer): THash {.inline.} = proc hash*(x: pointer): Hash {.inline.} =
## efficient hashing of pointers ## efficient hashing of pointers
when defined(js): when defined(js):
asm """ asm """
@ -92,50 +93,57 @@ proc hash*(x: pointer): THash {.inline.} =
} }
""" """
else: else:
result = (cast[THash](x)) shr 3 # skip the alignment result = (cast[Hash](x)) shr 3 # skip the alignment
when not defined(booting): when not defined(booting):
proc hash*[T: proc](x: T): THash {.inline.} = proc hash*[T: proc](x: T): Hash {.inline.} =
## efficient hashing of proc vars; closures are supported too. ## efficient hashing of proc vars; closures are supported too.
when T is "closure": when T is "closure":
result = hash(rawProc(x)) !& hash(rawEnv(x)) result = hash(rawProc(x)) !& hash(rawEnv(x))
else: else:
result = hash(pointer(x)) result = hash(pointer(x))
proc hash*(x: int): THash {.inline.} = proc hash*(x: int): Hash {.inline.} =
## efficient hashing of integers ## efficient hashing of integers
result = x result = x
proc hash*(x: int64): THash {.inline.} = proc hash*(x: int64): Hash {.inline.} =
## efficient hashing of integers ## efficient hashing of integers
result = toU32(x) result = toU32(x)
proc hash*(x: char): THash {.inline.} = proc hash*(x: char): Hash {.inline.} =
## efficient hashing of characters ## efficient hashing of characters
result = ord(x) result = ord(x)
proc hash*(x: string): THash = proc hash*(x: string): Hash =
## efficient hashing of strings ## efficient hashing of strings
var h: THash = 0 var h: Hash = 0
for i in 0..x.len-1: for i in 0..x.len-1:
h = h !& ord(x[i]) h = h !& ord(x[i])
result = !$h result = !$h
proc hashIgnoreStyle*(x: string): THash = proc hashIgnoreStyle*(x: string): Hash =
## efficient hashing of strings; style is ignored ## efficient hashing of strings; style is ignored
var h: THash = 0 var h: Hash = 0
for i in 0..x.len-1: var i = 0
let xLen = x.len
while i < xLen:
var c = x[i] var c = x[i]
if c == '_': if c == '_':
continue # skip _ inc(i)
if c in {'A'..'Z'}: elif isMagicIdentSeparatorRune(cstring(x), i):
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() inc(i, magicIdentSeparatorRuneByteWidth)
h = h !& ord(c) else:
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
inc(i)
result = !$h result = !$h
proc hashIgnoreCase*(x: string): THash = proc hashIgnoreCase*(x: string): Hash =
## efficient hashing of strings; case is ignored ## efficient hashing of strings; case is ignored
var h: THash = 0 var h: Hash = 0
for i in 0..x.len-1: for i in 0..x.len-1:
var c = x[i] var c = x[i]
if c in {'A'..'Z'}: if c in {'A'..'Z'}:
@ -143,28 +151,28 @@ proc hashIgnoreCase*(x: string): THash =
h = h !& ord(c) h = h !& ord(c)
result = !$h result = !$h
proc hash*(x: float): THash {.inline.} = proc hash*(x: float): Hash {.inline.} =
var y = x + 1.0 var y = x + 1.0
result = cast[ptr THash](addr(y))[] result = cast[ptr Hash](addr(y))[]
# Forward declarations before methods that hash containers. This allows # Forward declarations before methods that hash containers. This allows
# containers to contain other containers # containers to contain other containers
proc hash*[A](x: openArray[A]): THash proc hash*[A](x: openArray[A]): Hash
proc hash*[A](x: set[A]): THash proc hash*[A](x: set[A]): Hash
proc hash*[T: tuple](x: T): THash = proc hash*[T: tuple](x: T): Hash =
## efficient hashing of tuples. ## efficient hashing of tuples.
for f in fields(x): for f in fields(x):
result = result !& hash(f) result = result !& hash(f)
result = !$result result = !$result
proc hash*[A](x: openArray[A]): THash = proc hash*[A](x: openArray[A]): Hash =
for it in items(x): result = result !& hash(it) for it in items(x): result = result !& hash(it)
result = !$result result = !$result
proc hash*[A](x: set[A]): THash = proc hash*[A](x: set[A]): Hash =
for it in items(x): result = result !& hash(it) for it in items(x): result = result !& hash(it)
result = !$result result = !$result

View file

@ -52,7 +52,7 @@
import strutils, streams, parsexml, xmltree, unicode, strtabs import strutils, streams, parsexml, xmltree, unicode, strtabs
type type
THtmlTag* = enum ## list of all supported HTML tags; order will always be HtmlTag* = enum ## list of all supported HTML tags; order will always be
## alphabetically ## alphabetically
tagUnknown, ## unknown HTML element tagUnknown, ## unknown HTML element
tagA, ## the HTML ``a`` element tagA, ## the HTML ``a`` element
@ -178,6 +178,7 @@ type
tagVar, ## the HTML ``var`` element tagVar, ## the HTML ``var`` element
tagVideo, ## the HTML ``video`` element tagVideo, ## the HTML ``video`` element
tagWbr ## the HTML ``wbr`` element tagWbr ## the HTML ``wbr`` element
{.deprecated: [THtmlTag: HtmlTag].}
const const
tagToStr* = [ tagToStr* = [
@ -295,7 +296,7 @@ proc allLower(s: string): bool =
if c < 'a' or c > 'z': return false if c < 'a' or c > 'z': return false
return true return true
proc toHtmlTag(s: string): THtmlTag = proc toHtmlTag(s: string): HtmlTag =
case s case s
of "a": tagA of "a": tagA
of "abbr": tagAbbr of "abbr": tagAbbr
@ -422,14 +423,14 @@ proc toHtmlTag(s: string): THtmlTag =
of "wbr": tagWbr of "wbr": tagWbr
else: tagUnknown else: tagUnknown
proc htmlTag*(n: XmlNode): THtmlTag = proc htmlTag*(n: XmlNode): HtmlTag =
## gets `n`'s tag as a ``THtmlTag``. ## gets `n`'s tag as a ``HtmlTag``.
if n.clientData == 0: if n.clientData == 0:
n.clientData = toHtmlTag(n.tag).ord n.clientData = toHtmlTag(n.tag).ord
result = THtmlTag(n.clientData) result = HtmlTag(n.clientData)
proc htmlTag*(s: string): THtmlTag = proc htmlTag*(s: string): HtmlTag =
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is ## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
## returned. ## returned.
let s = if allLower(s): s else: s.toLower let s = if allLower(s): s else: s.toLower
result = toHtmlTag(s) result = toHtmlTag(s)

View file

@ -106,9 +106,10 @@ proc serveFile*(client: Socket, filename: string) =
when false: when false:
# TODO: Fix this, or get rid of it. # TODO: Fix this, or get rid of it.
type type
TRequestMethod = enum reqGet, reqPost RequestMethod = enum reqGet, reqPost
{.deprecated: [TRequestMethod: RequestMethod].}
proc executeCgi(client: Socket, path, query: string, meth: TRequestMethod) = proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
var env = newStringTable(modeCaseInsensitive) var env = newStringTable(modeCaseInsensitive)
var contentLength = -1 var contentLength = -1
case meth case meth
@ -208,7 +209,7 @@ when false:
executeCgi(client, path, query, meth) executeCgi(client, path, query, meth)
type type
TServer* = object of RootObj ## contains the current server state Server* = object of RootObj ## contains the current server state
socket: Socket socket: Socket
port: Port port: Port
client*: Socket ## the socket to write the file data to client*: Socket ## the socket to write the file data to
@ -218,11 +219,12 @@ type
body*: string ## only set with POST requests body*: string ## only set with POST requests
ip*: string ## ip address of the requesting client ip*: string ## ip address of the requesting client
PAsyncHTTPServer* = ref TAsyncHTTPServer PAsyncHTTPServer* = ref AsyncHTTPServer
TAsyncHTTPServer = object of TServer AsyncHTTPServer = object of Server
asyncSocket: AsyncSocket asyncSocket: AsyncSocket
{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
proc open*(s: var TServer, port = Port(80), reuseAddr = false) = proc open*(s: var Server, port = Port(80), reuseAddr = false) =
## creates a new server at port `port`. If ``port == 0`` a free port is ## creates a new server at port `port`. If ``port == 0`` a free port is
## acquired that can be accessed later by the ``port`` proc. ## acquired that can be accessed later by the ``port`` proc.
s.socket = socket(AF_INET) s.socket = socket(AF_INET)
@ -243,11 +245,11 @@ proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
s.query = "" s.query = ""
s.headers = {:}.newStringTable() s.headers = {:}.newStringTable()
proc port*(s: var TServer): Port = proc port*(s: var Server): Port =
## get the port number the server has acquired. ## get the port number the server has acquired.
result = s.port result = s.port
proc next*(s: var TServer) = proc next*(s: var Server) =
## proceed to the first/next request. ## proceed to the first/next request.
var client: Socket var client: Socket
new(client) new(client)
@ -354,7 +356,7 @@ proc next*(s: var TServer) =
s.query = "" s.query = ""
s.path = data.substr(i, last-1) s.path = data.substr(i, last-1)
proc close*(s: TServer) = proc close*(s: Server) =
## closes the server (and the socket the server uses). ## closes the server (and the socket the server uses).
close(s.socket) close(s.socket)
@ -362,7 +364,7 @@ proc run*(handleRequest: proc (client: Socket,
path, query: string): bool {.closure.}, path, query: string): bool {.closure.},
port = Port(80)) = port = Port(80)) =
## encapsulates the server object and main loop ## encapsulates the server object and main loop
var s: TServer var s: Server
open(s, port, reuseAddr = true) open(s, port, reuseAddr = true)
#echo("httpserver running on port ", s.port) #echo("httpserver running on port ", s.port)
while true: while true:
@ -517,7 +519,7 @@ proc close*(h: PAsyncHTTPServer) =
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
var counter = 0 var counter = 0
var s: TServer var s: Server
open(s, Port(0)) open(s, Port(0))
echo("httpserver running on port ", s.port) echo("httpserver running on port ", s.port)
while true: while true:

View file

@ -68,7 +68,7 @@ type
jsonArrayStart, ## start of an array: the ``[`` token jsonArrayStart, ## start of an array: the ``[`` token
jsonArrayEnd ## start of an array: the ``]`` token jsonArrayEnd ## start of an array: the ``]`` token
TTokKind = enum # must be synchronized with TJsonEventKind! TokKind = enum # must be synchronized with TJsonEventKind!
tkError, tkError,
tkEof, tkEof,
tkString, tkString,
@ -103,14 +103,14 @@ type
JsonParser* = object of BaseLexer ## the parser object. JsonParser* = object of BaseLexer ## the parser object.
a: string a: string
tok: TTokKind tok: TokKind
kind: JsonEventKind kind: JsonEventKind
err: JsonError err: JsonError
state: seq[ParserState] state: seq[ParserState]
filename: string filename: string
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError, {.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
TJsonParser: JsonParser].} TJsonParser: JsonParser, TTokKind: TokKind].}
const const
errorMessages: array [JsonError, string] = [ errorMessages: array [JsonError, string] = [
@ -126,7 +126,7 @@ const
"EOF expected", "EOF expected",
"expression expected" "expression expected"
] ]
tokToStr: array [TTokKind, string] = [ tokToStr: array [TokKind, string] = [
"invalid token", "invalid token",
"EOF", "EOF",
"string literal", "string literal",
@ -203,7 +203,7 @@ proc handleHexChar(c: char, x: var int): bool =
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: result = false # error else: result = false # error
proc parseString(my: var JsonParser): TTokKind = proc parseString(my: var JsonParser): TokKind =
result = tkString result = tkString
var pos = my.bufpos + 1 var pos = my.bufpos + 1
var buf = my.buf var buf = my.buf
@ -359,7 +359,7 @@ proc parseName(my: var JsonParser) =
inc(pos) inc(pos)
my.bufpos = pos my.bufpos = pos
proc getTok(my: var JsonParser): TTokKind = proc getTok(my: var JsonParser): TokKind =
setLen(my.a, 0) setLen(my.a, 0)
skip(my) # skip whitespace, comments skip(my) # skip whitespace, comments
case my.buf[my.bufpos] case my.buf[my.bufpos]
@ -734,7 +734,7 @@ proc `==`* (a,b: JsonNode): bool =
of JObject: of JObject:
a.fields == b.fields a.fields == b.fields
proc hash* (n:JsonNode): THash = proc hash* (n:JsonNode): Hash =
## Compute the hash for a JSON node ## Compute the hash for a JSON node
case n.kind case n.kind
of JArray: of JArray:
@ -1016,7 +1016,7 @@ iterator mpairs*(node: var JsonNode): var tuple[key: string, val: JsonNode] =
for keyVal in mitems(node.fields): for keyVal in mitems(node.fields):
yield keyVal yield keyVal
proc eat(p: var JsonParser, tok: TTokKind) = proc eat(p: var JsonParser, tok: TokKind) =
if p.tok == tok: discard getTok(p) if p.tok == tok: discard getTok(p)
else: raiseParseErr(p, tokToStr[tok]) else: raiseParseErr(p, tokToStr[tok])
@ -1091,8 +1091,10 @@ when not defined(js):
else: else:
from math import `mod` from math import `mod`
type type
TJSObject = object JSObject = object
proc parseNativeJson(x: cstring): TJSObject {.importc: "JSON.parse".} {.deprecated: [TJSObject: JSObject].}
proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
proc getVarType(x): JsonNodeKind = proc getVarType(x): JsonNodeKind =
result = JNull result = JNull
@ -1111,25 +1113,25 @@ else:
of "[object String]": return JString of "[object String]": return JString
else: assert false else: assert false
proc len(x: TJSObject): int = proc len(x: JSObject): int =
assert x.getVarType == JArray assert x.getVarType == JArray
asm """ asm """
return `x`.length; return `x`.length;
""" """
proc `[]`(x: TJSObject, y: string): TJSObject = proc `[]`(x: JSObject, y: string): JSObject =
assert x.getVarType == JObject assert x.getVarType == JObject
asm """ asm """
return `x`[`y`]; return `x`[`y`];
""" """
proc `[]`(x: TJSObject, y: int): TJSObject = proc `[]`(x: JSObject, y: int): JSObject =
assert x.getVarType == JArray assert x.getVarType == JArray
asm """ asm """
return `x`[`y`]; return `x`[`y`];
""" """
proc convertObject(x: TJSObject): JsonNode = proc convertObject(x: JSObject): JsonNode =
case getVarType(x) case getVarType(x)
of JArray: of JArray:
result = newJArray() result = newJArray()
@ -1141,7 +1143,7 @@ else:
if (`x`.hasOwnProperty(property)) { if (`x`.hasOwnProperty(property)) {
""" """
var nimProperty: cstring var nimProperty: cstring
var nimValue: TJSObject var nimValue: JSObject
asm "`nimProperty` = property; `nimValue` = `x`[property];" asm "`nimProperty` = property; `nimValue` = `x`[property];"
result[$nimProperty] = nimValue.convertObject() result[$nimProperty] = nimValue.convertObject()
asm "}}" asm "}}"

View file

@ -39,7 +39,7 @@ type
{.deprecated: [TBaseLexer: BaseLexer].} {.deprecated: [TBaseLexer: BaseLexer].}
proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192) proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192)
## inits the TBaseLexer with a stream to read from ## inits the BaseLexer with a stream to read from
proc close*(L: var BaseLexer) proc close*(L: var BaseLexer)
## closes the base lexer. This closes `L`'s associated stream too. ## closes the base lexer. This closes `L`'s associated stream too.

View file

@ -82,6 +82,7 @@ type
baseName: string # initial filename baseName: string # initial filename
baseMode: FileMode # initial file mode baseMode: FileMode # initial file mode
logFiles: int # how many log files already created, e.g. basename.1, basename.2... logFiles: int # how many log files already created, e.g. basename.1, basename.2...
bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
@ -112,27 +113,22 @@ proc substituteLog(frmt: string): string =
of "appname": result.add(app.splitFile.name) of "appname": result.add(app.splitFile.name)
else: discard else: discard
method log*(logger: Logger, level: Level, method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
frmt: string, args: varargs[string, `$`]) {.
raises: [Exception], raises: [Exception],
tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} = tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} =
## Override this method in custom loggers. Default implementation does ## Override this method in custom loggers. Default implementation does
## nothing. ## nothing.
discard discard
method log*(logger: ConsoleLogger, level: Level, method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
frmt: string, args: varargs[string, `$`]) =
## Logs to the console using ``logger`` only. ## Logs to the console using ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr), writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr), args)
frmt % args)
method log*(logger: FileLogger, level: Level, method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
frmt: string, args: varargs[string, `$`]) =
## Logs to a file using ``logger`` only. ## Logs to a file using ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
writeln(logger.f, LevelNames[level], " ", writeln(logger.f, LevelNames[level], " ", substituteLog(logger.fmtStr), args)
substituteLog(logger.fmtStr), frmt % args)
proc defaultFilename*(): string = proc defaultFilename*(): string =
## Returns the default filename for a logger. ## Returns the default filename for a logger.
@ -148,11 +144,14 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): Console
proc newFileLogger*(filename = defaultFilename(), proc newFileLogger*(filename = defaultFilename(),
mode: FileMode = fmAppend, mode: FileMode = fmAppend,
levelThreshold = lvlAll, levelThreshold = lvlAll,
fmtStr = defaultFmtStr): FileLogger = fmtStr = defaultFmtStr,
bufSize: int = -1): FileLogger =
## Creates a new file logger. This logger logs to a file. ## Creates a new file logger. This logger logs to a file.
## Use ``bufSize`` as size of the output buffer when writing the file
## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
new(result) new(result)
result.levelThreshold = levelThreshold result.levelThreshold = levelThreshold
result.f = open(filename, mode) result.f = open(filename, mode, bufSize = bufSize)
result.fmtStr = fmtStr result.fmtStr = fmtStr
# ------ # ------
@ -181,14 +180,18 @@ proc newRollingFileLogger*(filename = defaultFilename(),
mode: FileMode = fmReadWrite, mode: FileMode = fmReadWrite,
levelThreshold = lvlAll, levelThreshold = lvlAll,
fmtStr = defaultFmtStr, fmtStr = defaultFmtStr,
maxLines = 1000): RollingFileLogger = maxLines = 1000,
bufSize: int = -1): RollingFileLogger =
## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines ## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
## a new log file will be started and the old will be renamed. ## a new log file will be started and the old will be renamed.
## Use ``bufSize`` as size of the output buffer when writing the file
## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
new(result) new(result)
result.levelThreshold = levelThreshold result.levelThreshold = levelThreshold
result.fmtStr = fmtStr result.fmtStr = fmtStr
result.maxLines = maxLines result.maxLines = maxLines
result.f = open(filename, mode) result.bufSize = bufSize
result.f = open(filename, mode, bufSize=result.bufSize)
result.curLine = 0 result.curLine = 0
result.baseName = filename result.baseName = filename
result.baseMode = mode result.baseMode = mode
@ -206,8 +209,7 @@ proc rotate(logger: RollingFileLogger) =
moveFile(dir / (name & ext & srcSuff), moveFile(dir / (name & ext & srcSuff),
dir / (name & ext & ExtSep & $(i+1))) dir / (name & ext & ExtSep & $(i+1)))
method log*(logger: RollingFileLogger, level: Level, method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) =
frmt: string, args: varargs[string, `$`]) =
## Logs to a file using rolling ``logger`` only. ## Logs to a file using rolling ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
if logger.curLine >= logger.maxLines: if logger.curLine >= logger.maxLines:
@ -215,9 +217,9 @@ method log*(logger: RollingFileLogger, level: Level,
rotate(logger) rotate(logger)
logger.logFiles.inc logger.logFiles.inc
logger.curLine = 0 logger.curLine = 0
logger.f = open(logger.baseName, logger.baseMode) logger.f = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize)
writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args) writeln(logger.f, LevelNames[level], " ", substituteLog(logger.fmtStr), args)
logger.curLine.inc logger.curLine.inc
# -------- # --------
@ -225,39 +227,39 @@ method log*(logger: RollingFileLogger, level: Level,
var level {.threadvar.}: Level ## global log filter var level {.threadvar.}: Level ## global log filter
var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) = proc logLoop(level: Level, args: varargs[string, `$`]) =
for logger in items(handlers): for logger in items(handlers):
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
log(logger, level, frmt, args) log(logger, level, args)
template log*(level: Level, frmt: string, args: varargs[string, `$`]) = template log*(level: Level, args: varargs[string, `$`]) =
## Logs a message to all registered handlers at the given level. ## Logs a message to all registered handlers at the given level.
bind logLoop bind logLoop
bind `%` bind `%`
bind logging.level bind logging.level
if level >= logging.level: if level >= logging.level:
logLoop(level, frmt, args) logLoop(level, args)
template debug*(frmt: string, args: varargs[string, `$`]) = template debug*(args: varargs[string, `$`]) =
## Logs a debug message to all registered handlers. ## Logs a debug message to all registered handlers.
log(lvlDebug, frmt, args) log(lvlDebug, args)
template info*(frmt: string, args: varargs[string, `$`]) = template info*(args: varargs[string, `$`]) =
## Logs an info message to all registered handlers. ## Logs an info message to all registered handlers.
log(lvlInfo, frmt, args) log(lvlInfo, args)
template warn*(frmt: string, args: varargs[string, `$`]) = template warn*(args: varargs[string, `$`]) =
## Logs a warning message to all registered handlers. ## Logs a warning message to all registered handlers.
log(lvlWarn, frmt, args) log(lvlWarn, args)
template error*(frmt: string, args: varargs[string, `$`]) = template error*(args: varargs[string, `$`]) =
## Logs an error message to all registered handlers. ## Logs an error message to all registered handlers.
log(lvlError, frmt, args) log(lvlError, args)
template fatal*(frmt: string, args: varargs[string, `$`]) = template fatal*(args: varargs[string, `$`]) =
## Logs a fatal error message to all registered handlers. ## Logs a fatal error message to all registered handlers.
log(lvlFatal, frmt, args) log(lvlFatal, args)
proc addHandler*(handler: Logger) = proc addHandler*(handler: Logger) =
## Adds ``handler`` to the list of handlers. ## Adds ``handler`` to the list of handlers.
@ -286,6 +288,4 @@ when not defined(testing) and isMainModule:
addHandler(fL) addHandler(fL)
addHandler(rL) addHandler(rL)
for i in 0 .. 25: for i in 0 .. 25:
info("hello" & $i, []) info("hello", i)

View file

@ -17,13 +17,13 @@
## .. code-block:: nim ## .. code-block:: nim
## ##
## type ## type
## TA = object ## A = object
## TB = object of TA ## B = object of A
## f: int ## f: int
## ##
## var ## var
## a: ref TA ## a: ref A
## b: ref TB ## b: ref B
## ##
## new(b) ## new(b)
## a = b ## a = b
@ -36,7 +36,7 @@ import streams, typeinfo, json, intsets, tables
proc ptrToInt(x: pointer): int {.inline.} = proc ptrToInt(x: pointer): int {.inline.} =
result = cast[int](x) # don't skip alignment result = cast[int](x) # don't skip alignment
proc storeAny(s: Stream, a: TAny, stored: var IntSet) = proc storeAny(s: Stream, a: Any, stored: var IntSet) =
case a.kind case a.kind
of akNone: assert false of akNone: assert false
of akBool: s.write($getBool(a)) of akBool: s.write($getBool(a))
@ -96,7 +96,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a)) of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a))
of akFloat..akFloat128: s.write($getBiggestFloat(a)) of akFloat..akFloat128: s.write($getBiggestFloat(a))
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
case a.kind case a.kind
of akNone: assert false of akNone: assert false
of akBool: of akBool:
@ -222,7 +222,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
raiseParseErr(p, "float expected") raiseParseErr(p, "float expected")
of akRange: loadAny(p, a.skipRange, t) of akRange: loadAny(p, a.skipRange, t)
proc loadAny(s: Stream, a: TAny, t: var Table[BiggestInt, pointer]) = proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
var p: JsonParser var p: JsonParser
open(p, s, "unknown file") open(p, s, "unknown file")
next(p) next(p)
@ -278,10 +278,11 @@ when not defined(testing) and isMainModule:
else: else:
nil nil
PNode = ref TNode PNode = ref Node
TNode = object Node = object
next, prev: PNode next, prev: PNode
data: string data: string
{.deprecated: [TNode: Node].}
proc buildList(): PNode = proc buildList(): PNode =
new(result) new(result)
@ -317,14 +318,15 @@ when not defined(testing) and isMainModule:
testit(test7) testit(test7)
type type
TA {.inheritable.} = object A {.inheritable.} = object
TB = object of TA B = object of A
f: int f: int
var var
a: ref TA a: ref A
b: ref TB b: ref B
new(b) new(b)
a = b a = b
echo($$a[]) # produces "{}", not "{f: 0}" echo($$a[]) # produces "{}", not "{f: 0}"

View file

@ -196,7 +196,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
if mappedSize != -1: if mappedSize != -1:
result.size = mappedSize result.size = mappedSize
else: else:
var stat: TStat var stat: Stat
if fstat(result.handle, stat) != -1: if fstat(result.handle, stat) != -1:
# XXX: Hmm, this could be unsafe # XXX: Hmm, this could be unsafe
# Why is mmap taking int anyway? # Why is mmap taking int anyway?

View file

@ -86,7 +86,7 @@ type
IPv6, ## IPv6 address IPv6, ## IPv6 address
IPv4 ## IPv4 address IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address IpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6: of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
@ -94,9 +94,10 @@ type
of IpAddressFamily.IPv4: of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4 ## case of IPv4
{.deprecated: [TIpAddress: IpAddress].}
proc isIpAddress*(address_str: string): bool {.tags: [].} proc isIpAddress*(address_str: string): bool {.tags: [].}
proc parseIpAddress*(address_str: string): TIpAddress proc parseIpAddress*(address_str: string): IpAddress
proc isDisconnectionError*(flags: set[SocketFlag], proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool = lastError: OSErrorCode): bool =
@ -395,7 +396,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
when false: #defined(ssl): when false: #defined(ssl):
proc acceptAddrSSL*(server: Socket, client: var Socket, proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): TSSLAcceptResult {. address: var string): SSLAcceptResult {.
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## This procedure should only be used for non-blocking **SSL** sockets. ## This procedure should only be used for non-blocking **SSL** sockets.
## It will immediately return with one of the following values: ## It will immediately return with one of the following values:
@ -992,39 +993,39 @@ proc isSsl*(socket: Socket): bool =
proc getFd*(socket: Socket): SocketHandle = return socket.fd proc getFd*(socket: Socket): SocketHandle = return socket.fd
## Returns the socket's file descriptor ## Returns the socket's file descriptor
proc IPv4_any*(): TIpAddress = proc IPv4_any*(): IpAddress =
## Returns the IPv4 any address, which can be used to listen on all available ## Returns the IPv4 any address, which can be used to listen on all available
## network adapters ## network adapters
result = TIpAddress( result = IpAddress(
family: IpAddressFamily.IPv4, family: IpAddressFamily.IPv4,
address_v4: [0'u8, 0, 0, 0]) address_v4: [0'u8, 0, 0, 0])
proc IPv4_loopback*(): TIpAddress = proc IPv4_loopback*(): IpAddress =
## Returns the IPv4 loopback address (127.0.0.1) ## Returns the IPv4 loopback address (127.0.0.1)
result = TIpAddress( result = IpAddress(
family: IpAddressFamily.IPv4, family: IpAddressFamily.IPv4,
address_v4: [127'u8, 0, 0, 1]) address_v4: [127'u8, 0, 0, 1])
proc IPv4_broadcast*(): TIpAddress = proc IPv4_broadcast*(): IpAddress =
## Returns the IPv4 broadcast address (255.255.255.255) ## Returns the IPv4 broadcast address (255.255.255.255)
result = TIpAddress( result = IpAddress(
family: IpAddressFamily.IPv4, family: IpAddressFamily.IPv4,
address_v4: [255'u8, 255, 255, 255]) address_v4: [255'u8, 255, 255, 255])
proc IPv6_any*(): TIpAddress = proc IPv6_any*(): IpAddress =
## Returns the IPv6 any address (::0), which can be used ## Returns the IPv6 any address (::0), which can be used
## to listen on all available network adapters ## to listen on all available network adapters
result = TIpAddress( result = IpAddress(
family: IpAddressFamily.IPv6, family: IpAddressFamily.IPv6,
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
proc IPv6_loopback*(): TIpAddress = proc IPv6_loopback*(): IpAddress =
## Returns the IPv6 loopback address (::1) ## Returns the IPv6 loopback address (::1)
result = TIpAddress( result = IpAddress(
family: IpAddressFamily.IPv6, family: IpAddressFamily.IPv6,
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
proc `==`*(lhs, rhs: TIpAddress): bool = proc `==`*(lhs, rhs: IpAddress): bool =
## Compares two IpAddresses for Equality. Returns two if the addresses are equal ## Compares two IpAddresses for Equality. Returns two if the addresses are equal
if lhs.family != rhs.family: return false if lhs.family != rhs.family: return false
if lhs.family == IpAddressFamily.IPv4: if lhs.family == IpAddressFamily.IPv4:
@ -1035,8 +1036,8 @@ proc `==`*(lhs, rhs: TIpAddress): bool =
if lhs.address_v6[i] != rhs.address_v6[i]: return false if lhs.address_v6[i] != rhs.address_v6[i]: return false
return true return true
proc `$`*(address: TIpAddress): string = proc `$`*(address: IpAddress): string =
## Converts an TIpAddress into the textual representation ## Converts an IpAddress into the textual representation
result = "" result = ""
case address.family case address.family
of IpAddressFamily.IPv4: of IpAddressFamily.IPv4:
@ -1095,7 +1096,7 @@ proc `$`*(address: TIpAddress): string =
mask = mask shr 4 mask = mask shr 4
printedLastGroup = true printedLastGroup = true
proc parseIPv4Address(address_str: string): TIpAddress = proc parseIPv4Address(address_str: string): IpAddress =
## Parses IPv4 adresses ## Parses IPv4 adresses
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
var var
@ -1129,7 +1130,7 @@ proc parseIPv4Address(address_str: string): TIpAddress =
raise newException(ValueError, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte) result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): TIpAddress = proc parseIPv6Address(address_str: string): IpAddress =
## Parses IPv6 adresses ## Parses IPv6 adresses
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6 result.family = IpAddressFamily.IPv6
@ -1250,7 +1251,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
proc parseIpAddress(address_str: string): TIpAddress = proc parseIpAddress(address_str: string): IpAddress =
## Parses an IP address ## Parses an IP address
## Raises EInvalidValue on error ## Raises EInvalidValue on error
if address_str == nil: if address_str == nil:

View file

@ -26,17 +26,19 @@ const
withThreads = compileOption("threads") withThreads = compileOption("threads")
tickCountCorrection = 50_000 tickCountCorrection = 50_000
when not declared(system.TStackTrace): when not declared(system.StackTrace):
type TStackTrace = array [0..20, cstring] type StackTrace = array [0..20, cstring]
{.deprecated: [TStackTrace: StackTrace].}
# We use a simple hash table of bounded size to keep track of the stack traces: # We use a simple hash table of bounded size to keep track of the stack traces:
type type
TProfileEntry = object ProfileEntry = object
total: int total: int
st: TStackTrace st: StackTrace
TProfileData = array [0..64*1024-1, ptr TProfileEntry] ProfileData = array [0..64*1024-1, ptr ProfileEntry]
{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].}
proc `==`(a, b: TStackTrace): bool = proc `==`(a, b: StackTrace): bool =
for i in 0 .. high(a): for i in 0 .. high(a):
if a[i] != b[i]: return false if a[i] != b[i]: return false
result = true result = true
@ -44,13 +46,13 @@ proc `==`(a, b: TStackTrace): bool =
# XXX extract this data structure; it is generally useful ;-) # XXX extract this data structure; it is generally useful ;-)
# However a chain length of over 3000 is suspicious... # However a chain length of over 3000 is suspicious...
var var
profileData: TProfileData profileData: ProfileData
emptySlots = profileData.len * 3 div 2 emptySlots = profileData.len * 3 div 2
maxChainLen = 0 maxChainLen = 0
totalCalls = 0 totalCalls = 0
when not defined(memProfiler): when not defined(memProfiler):
var interval: TNanos = 5_000_000 - tickCountCorrection # 5ms var interval: Nanos = 5_000_000 - tickCountCorrection # 5ms
proc setSamplingFrequency*(intervalInUs: int) = proc setSamplingFrequency*(intervalInUs: int) =
## set this to change the sampling frequency. Default value is 5ms. ## set this to change the sampling frequency. Default value is 5ms.
@ -62,11 +64,11 @@ when not defined(memProfiler):
when withThreads: when withThreads:
import locks import locks
var var
profilingLock: TLock profilingLock: Lock
initLock profilingLock initLock profilingLock
proc hookAux(st: TStackTrace, costs: int) = proc hookAux(st: StackTrace, costs: int) =
# this is quite performance sensitive! # this is quite performance sensitive!
when withThreads: acquire profilingLock when withThreads: acquire profilingLock
inc totalCalls inc totalCalls
@ -94,8 +96,8 @@ proc hookAux(st: TStackTrace, costs: int) =
var chain = 0 var chain = 0
while true: while true:
if profileData[h] == nil: if profileData[h] == nil:
profileData[h] = cast[ptr TProfileEntry]( profileData[h] = cast[ptr ProfileEntry](
allocShared0(sizeof(TProfileEntry))) allocShared0(sizeof(ProfileEntry)))
profileData[h].total = costs profileData[h].total = costs
profileData[h].st = st profileData[h].st = st
dec emptySlots dec emptySlots
@ -115,7 +117,7 @@ when defined(memProfiler):
var var
gTicker {.threadvar.}: int gTicker {.threadvar.}: int
proc hook(st: TStackTrace, size: int) {.nimcall.} = proc hook(st: StackTrace, size: int) {.nimcall.} =
if gTicker == 0: if gTicker == 0:
gTicker = -1 gTicker = -1
when defined(ignoreAllocationSize): when defined(ignoreAllocationSize):
@ -127,26 +129,26 @@ when defined(memProfiler):
else: else:
var var
t0 {.threadvar.}: TTicks t0 {.threadvar.}: Ticks
proc hook(st: TStackTrace) {.nimcall.} = proc hook(st: StackTrace) {.nimcall.} =
if interval == 0: if interval == 0:
hookAux(st, 1) hookAux(st, 1)
elif int64(t0) == 0 or getTicks() - t0 > interval: elif int64(t0) == 0 or getTicks() - t0 > interval:
hookAux(st, 1) hookAux(st, 1)
t0 = getTicks() t0 = getTicks()
proc getTotal(x: ptr TProfileEntry): int = proc getTotal(x: ptr ProfileEntry): int =
result = if isNil(x): 0 else: x.total result = if isNil(x): 0 else: x.total
proc cmpEntries(a, b: ptr TProfileEntry): int = proc cmpEntries(a, b: ptr ProfileEntry): int =
result = b.getTotal - a.getTotal result = b.getTotal - a.getTotal
proc `//`(a, b: int): string = proc `//`(a, b: int): string =
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2)) result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
proc writeProfile() {.noconv.} = proc writeProfile() {.noconv.} =
when declared(system.TStackTrace): when declared(system.StackTrace):
system.profilerHook = nil system.profilerHook = nil
const filename = "profile_results.txt" const filename = "profile_results.txt"
echo "writing " & filename & "..." echo "writing " & filename & "..."
@ -161,7 +163,7 @@ proc writeProfile() {.noconv.} =
var perProc = initCountTable[string]() var perProc = initCountTable[string]()
for i in 0..entries-1: for i in 0..entries-1:
var dups = initSet[string]() var dups = initSet[string]()
for ii in 0..high(TStackTrace): for ii in 0..high(StackTrace):
let procname = profileData[i].st[ii] let procname = profileData[i].st[ii]
if isNil(procname): break if isNil(procname): break
let p = $procname let p = $procname
@ -176,7 +178,7 @@ proc writeProfile() {.noconv.} =
writeln(f, "Entry: ", i+1, "/", entries, " Calls: ", writeln(f, "Entry: ", i+1, "/", entries, " Calls: ",
profileData[i].total // totalCalls, " [sum: ", sum, "; ", profileData[i].total // totalCalls, " [sum: ", sum, "; ",
sum // totalCalls, "]") sum // totalCalls, "]")
for ii in 0..high(TStackTrace): for ii in 0..high(StackTrace):
let procname = profileData[i].st[ii] let procname = profileData[i].st[ii]
if isNil(procname): break if isNil(procname): break
writeln(f, " ", procname, " ", perProc[$procname] // totalCalls) writeln(f, " ", procname, " ", perProc[$procname] // totalCalls)
@ -189,16 +191,16 @@ var
disabled: int disabled: int
proc disableProfiling*() = proc disableProfiling*() =
when declared(system.TStackTrace): when declared(system.StackTrace):
atomicDec disabled atomicDec disabled
system.profilerHook = nil system.profilerHook = nil
proc enableProfiling*() = proc enableProfiling*() =
when declared(system.TStackTrace): when declared(system.StackTrace):
if atomicInc(disabled) >= 0: if atomicInc(disabled) >= 0:
system.profilerHook = hook system.profilerHook = hook
when declared(system.TStackTrace): when declared(system.StackTrace):
system.profilerHook = hook system.profilerHook = hook
addQuitProc(writeProfile) addQuitProc(writeProfile)

View file

@ -41,7 +41,7 @@ type
OSErrorCode* = distinct int32 ## Specifies an OS Error Code. OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect, {.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
FReadDir: ReadDirEffect, FReadDir: ReadDirEffect,
FWriteDir: WriteDirEffect, FWriteDir: WriteDirEffect,
TOSErrorCode: OSErrorCode TOSErrorCode: OSErrorCode
@ -359,7 +359,7 @@ when defined(windows):
template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} = template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} =
var varname = winApiProc(newWideCString(arg), arg2) var varname = winApiProc(newWideCString(arg), arg2)
proc findFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle = proc findFirstFile(a: string, b: var WIN32_FIND_DATA): Handle =
result = findFirstFileW(newWideCString(a), b) result = findFirstFileW(newWideCString(a), b)
template findNextFile(a, b: expr): expr = findNextFileW(a, b) template findNextFile(a, b: expr): expr = findNextFileW(a, b)
template getCommandLine(): expr = getCommandLineW() template getCommandLine(): expr = getCommandLineW()
@ -373,7 +373,7 @@ when defined(windows):
template getFilename(f: expr): expr = $f.cFilename template getFilename(f: expr): expr = $f.cFilename
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} = proc skipFindData(f: WIN32_FIND_DATA): bool {.inline.} =
# Note - takes advantage of null delimiter in the cstring # Note - takes advantage of null delimiter in the cstring
const dot = ord('.') const dot = ord('.')
result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
@ -390,7 +390,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
if a != -1'i32: if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32 result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
else: else:
var res: TStat var res: Stat
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} = proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
@ -404,7 +404,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect]
if a != -1'i32: if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
else: else:
var res: TStat var res: Stat
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
@ -419,7 +419,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
if a != -1'i32: if a != -1'i32:
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32 result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
else: else:
var res: TStat var res: Stat
return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
proc fileExists*(filename: string): bool {.inline.} = proc fileExists*(filename: string): bool {.inline.} =
@ -433,11 +433,11 @@ proc dirExists*(dir: string): bool {.inline.} =
proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} = proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
## Returns the `file`'s last modification time. ## Returns the `file`'s last modification time.
when defined(posix): when defined(posix):
var res: TStat var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError()) if stat(file, res) < 0'i32: raiseOSError(osLastError())
return res.st_mtime return res.st_mtime
else: else:
var f: TWIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError()) if h == -1'i32: raiseOSError(osLastError())
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)) result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
@ -446,11 +446,11 @@ proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
proc getLastAccessTime*(file: string): Time {.rtl, extern: "nos$1".} = proc getLastAccessTime*(file: string): Time {.rtl, extern: "nos$1".} =
## Returns the `file`'s last read or write access time. ## Returns the `file`'s last read or write access time.
when defined(posix): when defined(posix):
var res: TStat var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError()) if stat(file, res) < 0'i32: raiseOSError(osLastError())
return res.st_atime return res.st_atime
else: else:
var f: TWIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError()) if h == -1'i32: raiseOSError(osLastError())
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)) result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
@ -461,11 +461,11 @@ proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} =
## Note that under posix OS's, the returned time may actually be the time at ## Note that under posix OS's, the returned time may actually be the time at
## which the file's attribute's were last modified. ## which the file's attribute's were last modified.
when defined(posix): when defined(posix):
var res: TStat var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError()) if stat(file, res) < 0'i32: raiseOSError(osLastError())
return res.st_ctime return res.st_ctime
else: else:
var f: TWIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError()) if h == -1'i32: raiseOSError(osLastError())
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime)) result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
@ -794,20 +794,20 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
result = path[0] == '/' result = path[0] == '/'
when defined(Windows): when defined(Windows):
proc openHandle(path: string, followSymlink=true): THandle = proc openHandle(path: string, followSymlink=true): Handle =
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
if not followSymlink: if not followSymlink:
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
when useWinUnicode: when useWinUnicode:
result = createFileW( result = createFileW(
newWideCString(path), 0'i32, newWideCString(path), 0'i32,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0 nil, OPEN_EXISTING, flags, 0
) )
else: else:
result = createFileA( result = createFileA(
path, 0'i32, path, 0'i32,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0 nil, OPEN_EXISTING, flags, 0
) )
@ -827,7 +827,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
var lastErr: OSErrorCode var lastErr: OSErrorCode
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
var fi1, fi2: TBY_HANDLE_FILE_INFORMATION var fi1, fi2: BY_HANDLE_FILE_INFORMATION
if getFileInformationByHandle(f1, addr(fi1)) != 0 and if getFileInformationByHandle(f1, addr(fi1)) != 0 and
getFileInformationByHandle(f2, addr(fi2)) != 0: getFileInformationByHandle(f2, addr(fi2)) != 0:
@ -846,7 +846,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
if not success: raiseOSError(lastErr) if not success: raiseOSError(lastErr)
else: else:
var a, b: TStat var a, b: Stat
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32: if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:
@ -903,7 +903,7 @@ proc getFilePermissions*(filename: string): set[FilePermission] {.
## an error. On Windows, only the ``readonly`` flag is checked, every other ## an error. On Windows, only the ``readonly`` flag is checked, every other
## permission is available in any case. ## permission is available in any case.
when defined(posix): when defined(posix):
var a: TStat var a: Stat
if stat(filename, a) < 0'i32: raiseOSError(osLastError()) if stat(filename, a) < 0'i32: raiseOSError(osLastError())
result = {} result = {}
if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead) if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
@ -924,11 +924,11 @@ proc getFilePermissions*(filename: string): set[FilePermission] {.
var res = getFileAttributesA(filename) var res = getFileAttributesA(filename)
if res == -1'i32: raiseOSError(osLastError()) if res == -1'i32: raiseOSError(osLastError())
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
fpOthersExec, fpOthersRead} fpOthersExec, fpOthersRead}
else: else:
result = {fpUserExec..fpOthersRead} result = {fpUserExec..fpOthersRead}
proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
rtl, extern: "nos$1", tags: [WriteDirEffect].} = rtl, extern: "nos$1", tags: [WriteDirEffect].} =
## sets the file permissions for `filename`. `OSError` is raised in case of ## sets the file permissions for `filename`. `OSError` is raised in case of
@ -939,15 +939,15 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
if fpUserRead in permissions: p = p or S_IRUSR if fpUserRead in permissions: p = p or S_IRUSR
if fpUserWrite in permissions: p = p or S_IWUSR if fpUserWrite in permissions: p = p or S_IWUSR
if fpUserExec in permissions: p = p or S_IXUSR if fpUserExec in permissions: p = p or S_IXUSR
if fpGroupRead in permissions: p = p or S_IRGRP if fpGroupRead in permissions: p = p or S_IRGRP
if fpGroupWrite in permissions: p = p or S_IWGRP if fpGroupWrite in permissions: p = p or S_IWGRP
if fpGroupExec in permissions: p = p or S_IXGRP if fpGroupExec in permissions: p = p or S_IXGRP
if fpOthersRead in permissions: p = p or S_IROTH if fpOthersRead in permissions: p = p or S_IROTH
if fpOthersWrite in permissions: p = p or S_IWOTH if fpOthersWrite in permissions: p = p or S_IWOTH
if fpOthersExec in permissions: p = p or S_IXOTH if fpOthersExec in permissions: p = p or S_IXOTH
if chmod(filename, p) != 0: raiseOSError(osLastError()) if chmod(filename, p) != 0: raiseOSError(osLastError())
else: else:
when useWinUnicode: when useWinUnicode:
@ -955,7 +955,7 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
else: else:
var res = getFileAttributesA(filename) var res = getFileAttributesA(filename)
if res == -1'i32: raiseOSError(osLastError()) if res == -1'i32: raiseOSError(osLastError())
if fpUserWrite in permissions: if fpUserWrite in permissions:
res = res and not FILE_ATTRIBUTE_READONLY res = res and not FILE_ATTRIBUTE_READONLY
else: else:
res = res or FILE_ATTRIBUTE_READONLY res = res or FILE_ATTRIBUTE_READONLY
@ -1030,11 +1030,11 @@ when not declared(ENOENT) and not defined(Windows):
when defined(Windows): when defined(Windows):
when useWinUnicode: when useWinUnicode:
template deleteFile(file: expr): expr {.immediate.} = deleteFileW(file) template deleteFile(file: expr): expr {.immediate.} = deleteFileW(file)
template setFileAttributes(file, attrs: expr): expr {.immediate.} = template setFileAttributes(file, attrs: expr): expr {.immediate.} =
setFileAttributesW(file, attrs) setFileAttributesW(file, attrs)
else: else:
template deleteFile(file: expr): expr {.immediate.} = deleteFileA(file) template deleteFile(file: expr): expr {.immediate.} = deleteFileA(file)
template setFileAttributes(file, attrs: expr): expr {.immediate.} = template setFileAttributes(file, attrs: expr): expr {.immediate.} =
setFileAttributesA(file, attrs) setFileAttributesA(file, attrs)
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
@ -1047,7 +1047,7 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].}
else: else:
let f = file let f = file
if deleteFile(f) == 0: if deleteFile(f) == 0:
if getLastError() == ERROR_ACCESS_DENIED: if getLastError() == ERROR_ACCESS_DENIED:
if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0: if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
if deleteFile(f) == 0: if deleteFile(f) == 0:
@ -1220,7 +1220,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
## notation is supported. ## notation is supported.
when defined(windows): when defined(windows):
var var
f: TWIN32_FIND_DATA f: WIN32_FIND_DATA
res: int res: int
res = findFirstFile(pattern, f) res = findFirstFile(pattern, f)
if res != -1: if res != -1:
@ -1232,7 +1232,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
findClose(res) findClose(res)
else: # here we use glob else: # here we use glob
var var
f: TGlob f: Glob
res: int res: int
f.gl_offs = 0 f.gl_offs = 0
f.gl_pathc = 0 f.gl_pathc = 0
@ -1276,7 +1276,7 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {.
## dirA/fileA1.txt ## dirA/fileA1.txt
## dirA/fileA2.txt ## dirA/fileA2.txt
when defined(windows): when defined(windows):
var f: TWIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(dir / "*", f) var h = findFirstFile(dir / "*", f)
if h != -1: if h != -1:
while true: while true:
@ -1297,7 +1297,7 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {.
if x == nil: break if x == nil: break
var y = $x.d_name var y = $x.d_name
if y != "." and y != "..": if y != "." and y != "..":
var s: TStat var s: Stat
y = dir / y y = dir / y
var k = pcFile var k = pcFile
@ -1319,9 +1319,9 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
## walks over the directory `dir` and yields for each file in `dir`. The ## walks over the directory `dir` and yields for each file in `dir`. The
## full path for each file is returned. ## full path for each file is returned.
## **Warning**: ## **Warning**:
## Modifying the directory structure while the iterator ## Modifying the directory structure while the iterator
## is traversing may result in undefined behavior! ## is traversing may result in undefined behavior!
## ##
## Walking is recursive. `filter` controls the behaviour of the iterator: ## Walking is recursive. `filter` controls the behaviour of the iterator:
## ##
## --------------------- --------------------------------------------- ## --------------------- ---------------------------------------------
@ -1424,7 +1424,7 @@ proc createSymlink*(src, dest: string) =
## by `src`. On most operating systems, will fail if a lonk ## by `src`. On most operating systems, will fail if a lonk
## ##
## **Warning**: ## **Warning**:
## Some OS's (such as Microsoft Windows) restrict the creation ## Some OS's (such as Microsoft Windows) restrict the creation
## of symlinks to root users (administrators). ## of symlinks to root users (administrators).
when defined(Windows): when defined(Windows):
let flag = dirExists(src).int32 let flag = dirExists(src).int32
@ -1444,7 +1444,7 @@ proc createHardlink*(src, dest: string) =
## Create a hard link at `dest` which points to the item specified ## Create a hard link at `dest` which points to the item specified
## by `src`. ## by `src`.
## ##
## **Warning**: Most OS's restrict the creation of hard links to ## **Warning**: Most OS's restrict the creation of hard links to
## root users (administrators) . ## root users (administrators) .
when defined(Windows): when defined(Windows):
when useWinUnicode: when useWinUnicode:
@ -1548,7 +1548,7 @@ proc parseCmdLine*(c: string): seq[string] {.
add(a, c[i]) add(a, c[i])
inc(i) inc(i)
add(result, a) add(result, a)
proc copyFileWithPermissions*(source, dest: string, proc copyFileWithPermissions*(source, dest: string,
ignorePermissionErrors = true) = ignorePermissionErrors = true) =
## Copies a file from `source` to `dest` preserving file permissions. ## Copies a file from `source` to `dest` preserving file permissions.
@ -1842,7 +1842,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
when defined(windows): when defined(windows):
winlean.sleep(int32(milsecs)) winlean.sleep(int32(milsecs))
else: else:
var a, b: Ttimespec var a, b: Timespec
a.tv_sec = Time(milsecs div 1000) a.tv_sec = Time(milsecs div 1000)
a.tv_nsec = (milsecs mod 1000) * 1000 * 1000 a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
discard posix.nanosleep(a, b) discard posix.nanosleep(a, b)
@ -1851,7 +1851,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## returns the file size of `file`. Can raise ``OSError``. ## returns the file size of `file`. Can raise ``OSError``.
when defined(windows): when defined(windows):
var a: TWIN32_FIND_DATA var a: WIN32_FIND_DATA
var resA = findFirstFile(file, a) var resA = findFirstFile(file, a)
if resA == -1: raiseOSError(osLastError()) if resA == -1: raiseOSError(osLastError())
result = rdFileSize(a) result = rdFileSize(a)
@ -1907,8 +1907,8 @@ when defined(Windows):
FileId* = int64 FileId* = int64
else: else:
type type
DeviceId* = TDev DeviceId* = Dev
FileId* = Tino FileId* = Ino
type type
FileInfo* = object FileInfo* = object
@ -1925,7 +1925,7 @@ type
template rawToFormalFileInfo(rawInfo, formalInfo): expr = template rawToFormalFileInfo(rawInfo, formalInfo): expr =
## Transforms the native file info structure into the one nim uses. ## Transforms the native file info structure into the one nim uses.
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
## or a 'TStat' structure on posix ## or a 'Stat' structure on posix
when defined(Windows): when defined(Windows):
template toTime(e): expr = winTimeToUnixTime(rdFileTime(e)) template toTime(e): expr = winTimeToUnixTime(rdFileTime(e))
template merge(a, b): expr = a or (b shl 32) template merge(a, b): expr = a or (b shl 32)
@ -1936,10 +1936,10 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr =
formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime) formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime)
formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime) formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime)
formalInfo.creationTime = toTime(rawInfo.ftCreationTime) formalInfo.creationTime = toTime(rawInfo.ftCreationTime)
# Retrieve basic permissions # Retrieve basic permissions
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec, formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec,
fpGroupRead, fpOthersExec, fpOthersRead} fpGroupRead, fpOthersExec, fpOthersRead}
else: else:
result.permissions = {fpUserExec..fpOthersRead} result.permissions = {fpUserExec..fpOthersRead}
@ -1953,7 +1953,7 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr =
else: else:
template checkAndIncludeMode(rawMode, formalMode: expr) = template checkAndIncludeMode(rawMode, formalMode: expr) =
if (rawInfo.st_mode and rawMode) != 0'i32: if (rawInfo.st_mode and rawMode) != 0'i32:
formalInfo.permissions.incl(formalMode) formalInfo.permissions.incl(formalMode)
formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino) formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
@ -1988,7 +1988,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
## is invalid, an error will be thrown. ## is invalid, an error will be thrown.
# Done: ID, Kind, Size, Permissions, Link Count # Done: ID, Kind, Size, Permissions, Link Count
when defined(Windows): when defined(Windows):
var rawInfo: TBY_HANDLE_FILE_INFORMATION var rawInfo: BY_HANDLE_FILE_INFORMATION
# We have to use the super special '_get_osfhandle' call (wrapped above) # We have to use the super special '_get_osfhandle' call (wrapped above)
# To transform the C file descripter to a native file handle. # To transform the C file descripter to a native file handle.
var realHandle = get_osfhandle(handle) var realHandle = get_osfhandle(handle)
@ -1996,7 +1996,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
raiseOSError(osLastError()) raiseOSError(osLastError())
rawToFormalFileInfo(rawInfo, result) rawToFormalFileInfo(rawInfo, result)
else: else:
var rawInfo: TStat var rawInfo: Stat
if fstat(handle, rawInfo) < 0'i32: if fstat(handle, rawInfo) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
rawToFormalFileInfo(rawInfo, result) rawToFormalFileInfo(rawInfo, result)
@ -2008,22 +2008,22 @@ proc getFileInfo*(file: File): FileInfo =
proc getFileInfo*(path: string, followSymlink = true): FileInfo = proc getFileInfo*(path: string, followSymlink = true): FileInfo =
## Retrieves file information for the file object pointed to by `path`. ## Retrieves file information for the file object pointed to by `path`.
## ##
## Due to intrinsic differences between operating systems, the information ## Due to intrinsic differences between operating systems, the information
## contained by the returned `FileInfo` structure will be slightly different ## contained by the returned `FileInfo` structure will be slightly different
## across platforms, and in some cases, incomplete or inaccurate. ## across platforms, and in some cases, incomplete or inaccurate.
## ##
## When `followSymlink` is true, symlinks are followed and the information ## When `followSymlink` is true, symlinks are followed and the information
## retrieved is information related to the symlink's target. Otherwise, ## retrieved is information related to the symlink's target. Otherwise,
## information on the symlink itself is retrieved. ## information on the symlink itself is retrieved.
## ##
## If the information cannot be retrieved, such as when the path doesn't ## If the information cannot be retrieved, such as when the path doesn't
## exist, or when permission restrictions prevent the program from retrieving ## exist, or when permission restrictions prevent the program from retrieving
## file information, an error will be thrown. ## file information, an error will be thrown.
when defined(Windows): when defined(Windows):
var var
handle = openHandle(path, followSymlink) handle = openHandle(path, followSymlink)
rawInfo: TBY_HANDLE_FILE_INFORMATION rawInfo: BY_HANDLE_FILE_INFORMATION
if handle == INVALID_HANDLE_VALUE: if handle == INVALID_HANDLE_VALUE:
raiseOSError(osLastError()) raiseOSError(osLastError())
if getFileInformationByHandle(handle, addr rawInfo) == 0: if getFileInformationByHandle(handle, addr rawInfo) == 0:
@ -2031,7 +2031,7 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo =
rawToFormalFileInfo(rawInfo, result) rawToFormalFileInfo(rawInfo, result)
discard closeHandle(handle) discard closeHandle(handle)
else: else:
var rawInfo: TStat var rawInfo: Stat
if followSymlink: if followSymlink:
if stat(path, rawInfo) < 0'i32: if stat(path, rawInfo) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -2044,7 +2044,7 @@ proc isHidden*(path: string): bool =
## Determines whether a given path is hidden or not. Returns false if the ## Determines whether a given path is hidden or not. Returns false if the
## file doesn't exist. The given path must be accessible from the current ## file doesn't exist. The given path must be accessible from the current
## working directory of the program. ## working directory of the program.
## ##
## On Windows, a file is hidden if the file's 'hidden' attribute is set. ## On Windows, a file is hidden if the file's 'hidden' attribute is set.
## On Unix-like systems, a file is hidden if it starts with a '.' (period) ## On Unix-like systems, a file is hidden if it starts with a '.' (period)
## and is not *just* '.' or '..' ' ." ## and is not *just* '.' or '..' ' ."

View file

@ -26,13 +26,13 @@ when defined(linux):
type type
ProcessObj = object of RootObj ProcessObj = object of RootObj
when defined(windows): when defined(windows):
fProcessHandle: THandle fProcessHandle: Handle
inHandle, outHandle, errHandle: FileHandle inHandle, outHandle, errHandle: FileHandle
id: THandle id: Handle
else: else:
inHandle, outHandle, errHandle: FileHandle inHandle, outHandle, errHandle: FileHandle
inStream, outStream, errStream: Stream inStream, outStream, errStream: Stream
id: TPid id: Pid
exitCode: cint exitCode: cint
Process* = ref ProcessObj ## represents an operating system process Process* = ref ProcessObj ## represents an operating system process
@ -334,10 +334,11 @@ when not defined(useNimRtl):
when defined(Windows) and not defined(useNimRtl): when defined(Windows) and not defined(useNimRtl):
# We need to implement a handle stream for Windows: # We need to implement a handle stream for Windows:
type type
PFileHandleStream = ref TFileHandleStream PFileHandleStream = ref FileHandleStream
TFileHandleStream = object of StreamObj FileHandleStream = object of StreamObj
handle: THandle handle: Handle
atTheEnd: bool atTheEnd: bool
{.deprecated: [TFileHandleStream: FileHandleStream].}
proc hsClose(s: Stream) = discard # nothing to do here proc hsClose(s: Stream) = discard # nothing to do here
proc hsAtEnd(s: Stream): bool = return PFileHandleStream(s).atTheEnd proc hsAtEnd(s: Stream): bool = return PFileHandleStream(s).atTheEnd
@ -361,7 +362,7 @@ when defined(Windows) and not defined(useNimRtl):
addr bytesWritten, nil) addr bytesWritten, nil)
if a == 0: raiseOSError(osLastError()) if a == 0: raiseOSError(osLastError())
proc newFileHandleStream(handle: THandle): PFileHandleStream = proc newFileHandleStream(handle: Handle): PFileHandleStream =
new(result) new(result)
result.handle = handle result.handle = handle
result.closeImpl = hsClose result.closeImpl = hsClose
@ -387,22 +388,22 @@ when defined(Windows) and not defined(useNimRtl):
copyMem(addr(result[L]), cstring(x), x.len+1) # copy \0 copyMem(addr(result[L]), cstring(x), x.len+1) # copy \0
inc(L, x.len+1) inc(L, x.len+1)
#proc open_osfhandle(osh: THandle, mode: int): int {. #proc open_osfhandle(osh: Handle, mode: int): int {.
# importc: "_open_osfhandle", header: "<fcntl.h>".} # importc: "_open_osfhandle", header: "<fcntl.h>".}
#var #var
# O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int # O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
# O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int # O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
proc createPipeHandles(rdHandle, wrHandle: var THandle) = proc createPipeHandles(rdHandle, wrHandle: var Handle) =
var piInheritablePipe: TSECURITY_ATTRIBUTES var piInheritablePipe: SECURITY_ATTRIBUTES
piInheritablePipe.nLength = sizeof(TSECURITY_ATTRIBUTES).cint piInheritablePipe.nLength = sizeof(SECURITY_ATTRIBUTES).cint
piInheritablePipe.lpSecurityDescriptor = nil piInheritablePipe.lpSecurityDescriptor = nil
piInheritablePipe.bInheritHandle = 1 piInheritablePipe.bInheritHandle = 1
if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32: if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
proc fileClose(h: THandle) {.inline.} = proc fileClose(h: Handle) {.inline.} =
if h > 4: discard closeHandle(h) if h > 4: discard closeHandle(h)
proc startProcess(command: string, proc startProcess(command: string,
@ -411,10 +412,10 @@ when defined(Windows) and not defined(useNimRtl):
env: StringTableRef = nil, env: StringTableRef = nil,
options: set[ProcessOption] = {poStdErrToStdOut}): Process = options: set[ProcessOption] = {poStdErrToStdOut}): Process =
var var
si: TSTARTUPINFO si: STARTUPINFO
procInfo: TPROCESS_INFORMATION procInfo: PROCESS_INFORMATION
success: int success: int
hi, ho, he: THandle hi, ho, he: Handle
new(result) new(result)
si.cb = sizeof(si).cint si.cb = sizeof(si).cint
if poParentStreams notin options: if poParentStreams notin options:
@ -525,9 +526,9 @@ when defined(Windows) and not defined(useNimRtl):
proc execCmd(command: string): int = proc execCmd(command: string): int =
var var
si: TSTARTUPINFO si: STARTUPINFO
procInfo: TPROCESS_INFORMATION procInfo: PROCESS_INFORMATION
process: THandle process: Handle
L: int32 L: int32
si.cb = sizeof(si).cint si.cb = sizeof(si).cint
si.hStdError = getStdHandle(STD_ERROR_HANDLE) si.hStdError = getStdHandle(STD_ERROR_HANDLE)
@ -554,7 +555,7 @@ when defined(Windows) and not defined(useNimRtl):
proc select(readfds: var seq[Process], timeout = 500): int = proc select(readfds: var seq[Process], timeout = 500): int =
assert readfds.len <= MAXIMUM_WAIT_OBJECTS assert readfds.len <= MAXIMUM_WAIT_OBJECTS
var rfds: TWOHandleArray var rfds: WOHandleArray
for i in 0..readfds.len()-1: for i in 0..readfds.len()-1:
rfds[i] = readfds[i].fProcessHandle rfds[i] = readfds[i].fProcessHandle
@ -595,7 +596,7 @@ elif not defined(useNimRtl):
copyMem(result[i], addr(x[0]), x.len+1) copyMem(result[i], addr(x[0]), x.len+1)
inc(i) inc(i)
type TStartProcessData = object type StartProcessData = object
sysCommand: cstring sysCommand: cstring
sysArgs: cstringArray sysArgs: cstringArray
sysEnv: cstringArray sysEnv: cstringArray
@ -604,14 +605,15 @@ elif not defined(useNimRtl):
optionPoUsePath: bool optionPoUsePath: bool
optionPoParentStreams: bool optionPoParentStreams: bool
optionPoStdErrToStdOut: bool optionPoStdErrToStdOut: bool
{.deprecated: [TStartProcessData: StartProcessData].}
when not defined(useFork): when not defined(useFork):
proc startProcessAuxSpawn(data: TStartProcessData): TPid {. proc startProcessAuxSpawn(data: StartProcessData): Pid {.
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.} tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
proc startProcessAuxFork(data: TStartProcessData): TPid {. proc startProcessAuxFork(data: StartProcessData): Pid {.
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.} tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
{.push stacktrace: off, profiler: off.} {.push stacktrace: off, profiler: off.}
proc startProcessAfterFork(data: ptr TStartProcessData) {. proc startProcessAfterFork(data: ptr StartProcessData) {.
tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.} tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
{.pop.} {.pop.}
@ -641,7 +643,7 @@ elif not defined(useNimRtl):
for arg in args.items: for arg in args.items:
sysArgsRaw.add arg sysArgsRaw.add arg
var pid: TPid var pid: Pid
var sysArgs = allocCStringArray(sysArgsRaw) var sysArgs = allocCStringArray(sysArgsRaw)
defer: deallocCStringArray(sysArgs) defer: deallocCStringArray(sysArgs)
@ -653,7 +655,7 @@ elif not defined(useNimRtl):
defer: deallocCStringArray(sysEnv) defer: deallocCStringArray(sysEnv)
var data: TStartProcessData var data: StartProcessData
data.sysCommand = sysCommand data.sysCommand = sysCommand
data.sysArgs = sysArgs data.sysArgs = sysArgs
data.sysEnv = sysEnv data.sysEnv = sysEnv
@ -698,7 +700,7 @@ elif not defined(useNimRtl):
discard close(pStdout[writeIdx]) discard close(pStdout[writeIdx])
when not defined(useFork): when not defined(useFork):
proc startProcessAuxSpawn(data: TStartProcessData): TPid = proc startProcessAuxSpawn(data: StartProcessData): Pid =
var attr: Tposix_spawnattr var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions var fops: Tposix_spawn_file_actions
@ -708,7 +710,7 @@ elif not defined(useNimRtl):
chck posix_spawn_file_actions_init(fops) chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr) chck posix_spawnattr_init(attr)
var mask: Tsigset var mask: Sigset
chck sigemptyset(mask) chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask) chck posix_spawnattr_setsigmask(attr, mask)
chck posix_spawnattr_setpgroup(attr, 0'i32) chck posix_spawnattr_setpgroup(attr, 0'i32)
@ -732,7 +734,7 @@ elif not defined(useNimRtl):
# FIXME: chdir is global to process # FIXME: chdir is global to process
if data.workingDir.len > 0: if data.workingDir.len > 0:
setCurrentDir($data.workingDir) setCurrentDir($data.workingDir)
var pid: TPid var pid: Pid
if data.optionPoUsePath: if data.optionPoUsePath:
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
@ -744,14 +746,14 @@ elif not defined(useNimRtl):
chck res chck res
return pid return pid
proc startProcessAuxFork(data: TStartProcessData): TPid = proc startProcessAuxFork(data: StartProcessData): Pid =
if pipe(data.pErrorPipe) != 0: if pipe(data.pErrorPipe) != 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
defer: defer:
discard close(data.pErrorPipe[readIdx]) discard close(data.pErrorPipe[readIdx])
var pid: TPid var pid: Pid
var dataCopy = data var dataCopy = data
when defined(useClone): when defined(useClone):
@ -781,7 +783,7 @@ elif not defined(useNimRtl):
return pid return pid
{.push stacktrace: off, profiler: off.} {.push stacktrace: off, profiler: off.}
proc startProcessFail(data: ptr TStartProcessData) = proc startProcessFail(data: ptr StartProcessData) =
var error: cint = errno var error: cint = errno
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
exitnow(1) exitnow(1)
@ -789,7 +791,7 @@ elif not defined(useNimRtl):
when defined(macosx) or defined(freebsd): when defined(macosx) or defined(freebsd):
var environ {.importc.}: cstringArray var environ {.importc.}: cstringArray
proc startProcessAfterFork(data: ptr TStartProcessData) = proc startProcessAfterFork(data: ptr StartProcessData) =
# Warning: no GC here! # Warning: no GC here!
# Or anything that touches global structures - all called nim procs # Or anything that touches global structures - all called nim procs
# must be marked with stackTrace:off. Inspect C code after making changes. # must be marked with stackTrace:off. Inspect C code after making changes.

View file

@ -133,7 +133,7 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
when declared(initOptParser): when declared(initOptParser):
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] = iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the command line. ## This is an convenience iterator for iterating over the command line.
## This uses the TOptParser object. Example: ## This uses the OptParser object. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## var ## var

View file

@ -74,8 +74,8 @@ type
line: int ## line the symbol has been declared/used in line: int ## line the symbol has been declared/used in
col: int ## column the symbol has been declared/used in col: int ## column the symbol has been declared/used in
flags: set[NonTerminalFlag] ## the nonterminal's flags flags: set[NonTerminalFlag] ## the nonterminal's flags
rule: TNode ## the rule that the symbol refers to rule: Node ## the rule that the symbol refers to
TNode {.shallow.} = object Node {.shallow.} = object
case kind: PegKind case kind: PegKind
of pkEmpty..pkWhitespace: nil of pkEmpty..pkWhitespace: nil
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
@ -83,12 +83,12 @@ type
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
of pkNonTerminal: nt: NonTerminal of pkNonTerminal: nt: NonTerminal
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
else: sons: seq[TNode] else: sons: seq[Node]
NonTerminal* = ref NonTerminalObj NonTerminal* = ref NonTerminalObj
Peg* = TNode ## type that represents a PEG Peg* = Node ## type that represents a PEG
{.deprecated: [TPeg: Peg].} {.deprecated: [TPeg: Peg, TNode: Node].}
proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
## constructs a PEG from a terminal string ## constructs a PEG from a terminal string
@ -1014,12 +1014,12 @@ proc split*(s: string, sep: Peg): seq[string] {.
# ------------------- scanner ------------------------------------------------- # ------------------- scanner -------------------------------------------------
type type
TModifier = enum Modifier = enum
modNone, modNone,
modVerbatim, modVerbatim,
modIgnoreCase, modIgnoreCase,
modIgnoreStyle modIgnoreStyle
TTokKind = enum ## enumeration of all tokens TokKind = enum ## enumeration of all tokens
tkInvalid, ## invalid token tkInvalid, ## invalid token
tkEof, ## end of file reached tkEof, ## end of file reached
tkAny, ## . tkAny, ## .
@ -1046,9 +1046,9 @@ type
tkDollar, ## '$' tkDollar, ## '$'
tkHat ## '^' tkHat ## '^'
TToken {.final.} = object ## a token Token {.final.} = object ## a token
kind: TTokKind ## the type of the token kind: TokKind ## the type of the token
modifier: TModifier modifier: Modifier
literal: string ## the parsed (string) literal literal: string ## the parsed (string) literal
charset: set[char] ## if kind == tkCharSet charset: set[char] ## if kind == tkCharSet
index: int ## if kind == tkBackref index: int ## if kind == tkBackref
@ -1060,9 +1060,10 @@ type
lineStart: int ## index of last line start in buffer lineStart: int ## index of last line start in buffer
colOffset: int ## column to add colOffset: int ## column to add
filename: string filename: string
{.deprecated: [TTokKind: TokKind, TToken: Token, TModifier: Modifier].}
const const
tokKindToStr: array[TTokKind, string] = [ tokKindToStr: array[TokKind, string] = [
"invalid", "[EOF]", ".", "_", "identifier", "string literal", "invalid", "[EOF]", ".", "_", "identifier", "string literal",
"character set", "(", ")", "{", "}", "{@}", "character set", "(", ")", "{", "}", "{@}",
"<-", "/", "*", "+", "&", "!", "?", "<-", "/", "*", "+", "&", "!", "?",
@ -1114,7 +1115,7 @@ proc handleHexChar(c: var PegLexer, xi: var int) =
inc(c.bufpos) inc(c.bufpos)
else: discard else: discard
proc getEscapedChar(c: var PegLexer, tok: var TToken) = proc getEscapedChar(c: var PegLexer, tok: var Token) =
inc(c.bufpos) inc(c.bufpos)
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'r', 'R', 'c', 'C': of 'r', 'R', 'c', 'C':
@ -1185,7 +1186,7 @@ proc skip(c: var PegLexer) =
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc getString(c: var PegLexer, tok: var TToken) = proc getString(c: var PegLexer, tok: var Token) =
tok.kind = tkStringLit tok.kind = tkStringLit
var pos = c.bufpos + 1 var pos = c.bufpos + 1
var buf = c.buf var buf = c.buf
@ -1207,7 +1208,7 @@ proc getString(c: var PegLexer, tok: var TToken) =
inc(pos) inc(pos)
c.bufpos = pos c.bufpos = pos
proc getDollar(c: var PegLexer, tok: var TToken) = proc getDollar(c: var PegLexer, tok: var Token) =
var pos = c.bufpos + 1 var pos = c.bufpos + 1
var buf = c.buf var buf = c.buf
if buf[pos] in {'0'..'9'}: if buf[pos] in {'0'..'9'}:
@ -1220,7 +1221,7 @@ proc getDollar(c: var PegLexer, tok: var TToken) =
tok.kind = tkDollar tok.kind = tkDollar
c.bufpos = pos c.bufpos = pos
proc getCharSet(c: var PegLexer, tok: var TToken) = proc getCharSet(c: var PegLexer, tok: var Token) =
tok.kind = tkCharSet tok.kind = tkCharSet
tok.charset = {} tok.charset = {}
var pos = c.bufpos + 1 var pos = c.bufpos + 1
@ -1271,7 +1272,7 @@ proc getCharSet(c: var PegLexer, tok: var TToken) =
c.bufpos = pos c.bufpos = pos
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
proc getSymbol(c: var PegLexer, tok: var TToken) = proc getSymbol(c: var PegLexer, tok: var Token) =
var pos = c.bufpos var pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
@ -1281,7 +1282,7 @@ proc getSymbol(c: var PegLexer, tok: var TToken) =
c.bufpos = pos c.bufpos = pos
tok.kind = tkIdentifier tok.kind = tkIdentifier
proc getBuiltin(c: var PegLexer, tok: var TToken) = proc getBuiltin(c: var PegLexer, tok: var Token) =
if c.buf[c.bufpos+1] in strutils.Letters: if c.buf[c.bufpos+1] in strutils.Letters:
inc(c.bufpos) inc(c.bufpos)
getSymbol(c, tok) getSymbol(c, tok)
@ -1290,7 +1291,7 @@ proc getBuiltin(c: var PegLexer, tok: var TToken) =
tok.kind = tkEscaped tok.kind = tkEscaped
getEscapedChar(c, tok) # may set tok.kind to tkInvalid getEscapedChar(c, tok) # may set tok.kind to tkInvalid
proc getTok(c: var PegLexer, tok: var TToken) = proc getTok(c: var PegLexer, tok: var Token) =
tok.kind = tkInvalid tok.kind = tkInvalid
tok.modifier = modNone tok.modifier = modNone
setLen(tok.literal, 0) setLen(tok.literal, 0)
@ -1408,9 +1409,9 @@ type
EInvalidPeg* = object of ValueError ## raised if an invalid EInvalidPeg* = object of ValueError ## raised if an invalid
## PEG has been detected ## PEG has been detected
PegParser = object of PegLexer ## the PEG parser object PegParser = object of PegLexer ## the PEG parser object
tok: TToken tok: Token
nonterms: seq[NonTerminal] nonterms: seq[NonTerminal]
modifier: TModifier modifier: Modifier
captures: int captures: int
identIsVerbatim: bool identIsVerbatim: bool
skip: Peg skip: Peg
@ -1425,7 +1426,7 @@ proc getTok(p: var PegParser) =
getTok(p, p.tok) getTok(p, p.tok)
if p.tok.kind == tkInvalid: pegError(p, "invalid token") if p.tok.kind == tkInvalid: pegError(p, "invalid token")
proc eat(p: var PegParser, kind: TTokKind) = proc eat(p: var PegParser, kind: TokKind) =
if p.tok.kind == kind: getTok(p) if p.tok.kind == kind: getTok(p)
else: pegError(p, tokKindToStr[kind] & " expected") else: pegError(p, tokKindToStr[kind] & " expected")
@ -1439,13 +1440,13 @@ proc getNonTerminal(p: var PegParser, name: string): NonTerminal =
result = newNonTerminal(name, getLine(p), getColumn(p)) result = newNonTerminal(name, getLine(p), getColumn(p))
add(p.nonterms, result) add(p.nonterms, result)
proc modifiedTerm(s: string, m: TModifier): Peg = proc modifiedTerm(s: string, m: Modifier): Peg =
case m case m
of modNone, modVerbatim: result = term(s) of modNone, modVerbatim: result = term(s)
of modIgnoreCase: result = termIgnoreCase(s) of modIgnoreCase: result = termIgnoreCase(s)
of modIgnoreStyle: result = termIgnoreStyle(s) of modIgnoreStyle: result = termIgnoreStyle(s)
proc modifiedBackref(s: int, m: TModifier): Peg = proc modifiedBackref(s: int, m: Modifier): Peg =
case m case m
of modNone, modVerbatim: result = backref(s) of modNone, modVerbatim: result = backref(s)
of modIgnoreCase: result = backrefIgnoreCase(s) of modIgnoreCase: result = backrefIgnoreCase(s)

View file

@ -206,13 +206,13 @@ proc abs*[T](x: Rational[T]): Rational[T] =
result.num = abs x.num result.num = abs x.num
result.den = abs x.den result.den = abs x.den
proc hash*[T](x: Rational[T]): THash = proc hash*[T](x: Rational[T]): Hash =
## Computes hash for rational `x` ## Computes hash for rational `x`
# reduce first so that hash(x) == hash(y) for x == y # reduce first so that hash(x) == hash(y) for x == y
var copy = x var copy = x
reduce(copy) reduce(copy)
var h: THash = 0 var h: Hash = 0
h = h !& hash(copy.num) h = h !& hash(copy.num)
h = h !& hash(copy.den) h = h !& hash(copy.den)
result = !$h result = !$h

View file

@ -106,13 +106,13 @@ proc `$`*(p: Port): string {.borrow.}
## returns the port number as a string ## returns the port number as a string
proc toInt*(domain: Domain): cint proc toInt*(domain: Domain): cint
## Converts the TDomain enum to a platform-dependent ``cint``. ## Converts the Domain enum to a platform-dependent ``cint``.
proc toInt*(typ: SockType): cint proc toInt*(typ: SockType): cint
## Converts the TType enum to a platform-dependent ``cint``. ## Converts the SockType enum to a platform-dependent ``cint``.
proc toInt*(p: Protocol): cint proc toInt*(p: Protocol): cint
## Converts the TProtocol enum to a platform-dependent ``cint``. ## Converts the Protocol enum to a platform-dependent ``cint``.
when not useWinVersion: when not useWinVersion:
proc toInt(domain: Domain): cint = proc toInt(domain: Domain): cint =

View file

@ -10,8 +10,8 @@
## This module implements a redis client. It allows you to connect to a ## This module implements a redis client. It allows you to connect to a
## redis-server instance, send commands and receive replies. ## redis-server instance, send commands and receive replies.
## ##
## **Beware**: Most (if not all) functions that return a ``TRedisString`` may ## **Beware**: Most (if not all) functions that return a ``RedisString`` may
## return ``redisNil``, and functions which return a ``TRedisList`` ## return ``redisNil``, and functions which return a ``RedisList``
## may return ``nil``. ## may return ``nil``.
import sockets, os, strutils, parseutils import sockets, os, strutils, parseutils
@ -843,27 +843,27 @@ proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) =
# TODO: pub/sub -- I don't think this will work synchronously. # TODO: pub/sub -- I don't think this will work synchronously.
discard """ discard """
proc psubscribe*(r: TRedis, pattern: openarray[string]): ???? = proc psubscribe*(r: Redis, pattern: openarray[string]): ???? =
## Listen for messages published to channels matching the given patterns ## Listen for messages published to channels matching the given patterns
r.socket.send("PSUBSCRIBE $#\c\L" % pattern) r.socket.send("PSUBSCRIBE $#\c\L" % pattern)
return ??? return ???
proc publish*(r: TRedis, channel: string, message: string): TRedisInteger = proc publish*(r: Redis, channel: string, message: string): RedisInteger =
## Post a message to a channel ## Post a message to a channel
r.socket.send("PUBLISH $# $#\c\L" % [channel, message]) r.socket.send("PUBLISH $# $#\c\L" % [channel, message])
return r.readInteger() return r.readInteger()
proc punsubscribe*(r: TRedis, [pattern: openarray[string], : string): ???? = proc punsubscribe*(r: Redis, [pattern: openarray[string], : string): ???? =
## Stop listening for messages posted to channels matching the given patterns ## Stop listening for messages posted to channels matching the given patterns
r.socket.send("PUNSUBSCRIBE $# $#\c\L" % [[pattern.join(), ]) r.socket.send("PUNSUBSCRIBE $# $#\c\L" % [[pattern.join(), ])
return ??? return ???
proc subscribe*(r: TRedis, channel: openarray[string]): ???? = proc subscribe*(r: Redis, channel: openarray[string]): ???? =
## Listen for messages published to the given channels ## Listen for messages published to the given channels
r.socket.send("SUBSCRIBE $#\c\L" % channel.join) r.socket.send("SUBSCRIBE $#\c\L" % channel.join)
return ??? return ???
proc unsubscribe*(r: TRedis, [channel: openarray[string], : string): ???? = proc unsubscribe*(r: Redis, [channel: openarray[string], : string): ???? =
## Stop listening for messages posted to the given channels ## Stop listening for messages posted to the given channels
r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ]) r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ])
return ??? return ???
@ -991,7 +991,7 @@ proc lastsave*(r: Redis): RedisInteger =
return r.readInteger() return r.readInteger()
discard """ discard """
proc monitor*(r: TRedis) = proc monitor*(r: Redis) =
## Listen for all requests received by the server in real time ## Listen for all requests received by the server in real time
r.socket.send("MONITOR\c\L") r.socket.send("MONITOR\c\L")
raiseNoOK(r.readStatus(), r.pipeline.enabled) raiseNoOK(r.readStatus(), r.pipeline.enabled)

View file

@ -18,7 +18,7 @@ elif defined(windows):
else: else:
import posix import posix
proc hash*(x: SocketHandle): THash {.borrow.} proc hash*(x: SocketHandle): Hash {.borrow.}
proc `$`*(x: SocketHandle): string {.borrow.} proc `$`*(x: SocketHandle): string {.borrow.}
type type
@ -41,7 +41,7 @@ when defined(nimdoc):
proc register*(s: Selector, fd: SocketHandle, events: set[Event], proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: RootRef): SelectorKey {.discardable.} = data: RootRef): SelectorKey {.discardable.} =
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## Registers file descriptor ``fd`` to selector ``s`` with a set of Event
## ``events``. ## ``events``.
proc update*(s: Selector, fd: SocketHandle, proc update*(s: Selector, fd: SocketHandle,

View file

@ -23,7 +23,7 @@ type
sexpListStart, ## start of a list: the ``(`` token sexpListStart, ## start of a list: the ``(`` token
sexpListEnd, ## end of a list: the ``)`` token sexpListEnd, ## end of a list: the ``)`` token
TTokKind = enum # must be synchronized with SexpEventKind! TokKind = enum # must be synchronized with SexpEventKind!
tkError, tkError,
tkEof, tkEof,
tkString, tkString,
@ -45,9 +45,10 @@ type
SexpParser* = object of BaseLexer ## the parser object. SexpParser* = object of BaseLexer ## the parser object.
a: string a: string
tok: TTokKind tok: TokKind
kind: SexpEventKind kind: SexpEventKind
err: SexpError err: SexpError
{.deprecated: [TTokKind: TokKind].}
const const
errorMessages: array [SexpError, string] = [ errorMessages: array [SexpError, string] = [
@ -57,7 +58,7 @@ const
"'\"' or \"'\" expected", "'\"' or \"'\" expected",
"EOF expected", "EOF expected",
] ]
tokToStr: array [TTokKind, string] = [ tokToStr: array [TokKind, string] = [
"invalid token", "invalid token",
"EOF", "EOF",
"string literal", "string literal",
@ -119,7 +120,7 @@ proc handleHexChar(c: char, x: var int): bool =
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: result = false # error else: result = false # error
proc parseString(my: var SexpParser): TTokKind = proc parseString(my: var SexpParser): TokKind =
result = tkString result = tkString
var pos = my.bufpos + 1 var pos = my.bufpos + 1
var buf = my.buf var buf = my.buf
@ -217,7 +218,7 @@ proc parseSymbol(my: var SexpParser) =
inc(pos) inc(pos)
my.bufpos = pos my.bufpos = pos
proc getTok(my: var SexpParser): TTokKind = proc getTok(my: var SexpParser): TokKind =
setLen(my.a, 0) setLen(my.a, 0)
case my.buf[my.bufpos] case my.buf[my.bufpos]
of '-', '0'..'9': # numbers that start with a . are not parsed of '-', '0'..'9': # numbers that start with a . are not parsed
@ -466,7 +467,7 @@ proc `==`* (a,b: SexpNode): bool =
of SCons: of SCons:
a.car == b.car and a.cdr == b.cdr a.car == b.car and a.cdr == b.cdr
proc hash* (n:SexpNode): THash = proc hash* (n:SexpNode): Hash =
## Compute the hash for a SEXP node ## Compute the hash for a SEXP node
case n.kind case n.kind
of SList: of SList:
@ -620,7 +621,7 @@ iterator mitems*(node: var SexpNode): var SexpNode =
for i in mitems(node.elems): for i in mitems(node.elems):
yield i yield i
proc eat(p: var SexpParser, tok: TTokKind) = proc eat(p: var SexpParser, tok: TokKind) =
if p.tok == tok: discard getTok(p) if p.tok == tok: discard getTok(p)
else: raiseParseErr(p, tokToStr[tok]) else: raiseParseErr(p, tokToStr[tok])

View file

@ -75,7 +75,7 @@ const
BufferSize*: int = 4000 ## size of a buffered socket's buffer BufferSize*: int = 4000 ## size of a buffered socket's buffer
type type
TSocketImpl = object ## socket type SocketImpl = object ## socket type
fd: SocketHandle fd: SocketHandle
case isBuffered: bool # determines whether this socket is buffered. case isBuffered: bool # determines whether this socket is buffered.
of true: of true:
@ -94,7 +94,7 @@ type
of false: nil of false: nil
nonblocking: bool nonblocking: bool
Socket* = ref TSocketImpl Socket* = ref SocketImpl
Port* = distinct uint16 ## port type Port* = distinct uint16 ## port type
@ -146,8 +146,9 @@ type
{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain, {.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
TProtocol: Protocol, TServent: Servent, THostent: Hostent, TProtocol: Protocol, TServent: Servent, THostent: Hostent,
TSOBool: SOBool, TRecvLineResult: RecvLineResult, TSOBool: SOBool, TRecvLineResult: RecvLineResult,
TReadLineResult: ReadLineResult, ETimeout: TimeoutError].} TReadLineResult: ReadLineResult, ETimeout: TimeoutError,
TSocketImpl: SocketImpl].}
when defined(booting): when defined(booting):
let invalidSocket*: Socket = nil ## invalid socket let invalidSocket*: Socket = nil ## invalid socket

View file

@ -327,7 +327,7 @@ proc newStringStream*(s: string = ""): StringStream =
when not defined(js): when not defined(js):
type type
FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile` FileStream* = ref FileStreamObj ## a stream that encapsulates a `File`
FileStreamObj* = object of Stream FileStreamObj* = object of Stream
f: File f: File
{.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].} {.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}

View file

@ -74,7 +74,7 @@ const
growthFactor = 2 growthFactor = 2
startSize = 64 startSize = 64
proc myhash(t: StringTableRef, key: string): THash = proc myhash(t: StringTableRef, key: string): Hash =
case t.mode case t.mode
of modeCaseSensitive: result = hashes.hash(key) of modeCaseSensitive: result = hashes.hash(key)
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key) of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
@ -90,11 +90,11 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash {.inline.} = proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash result = ((5 * h) + 1) and maxHash
proc rawGet(t: StringTableRef, key: string): int = proc rawGet(t: StringTableRef, key: string): int =
var h: THash = myhash(t, key) and high(t.data) # start with real hash value var h: Hash = myhash(t, key) and high(t.data) # start with real hash value
while not isNil(t.data[h].key): while not isNil(t.data[h].key):
if myCmp(t, t.data[h].key, key): if myCmp(t, t.data[h].key, key):
return h return h
@ -122,7 +122,7 @@ proc hasKey*(t: StringTableRef, key: string): bool {.rtl, extern: "nst$1".} =
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) = proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
var h: THash = myhash(t, key) and high(data) var h: Hash = myhash(t, key) and high(data)
while not isNil(data[h].key): while not isNil(data[h].key):
h = nextTry(h, high(data)) h = nextTry(h, high(data))
data[h].key = key data[h].key = key

View file

@ -23,7 +23,8 @@ import parseutils
include "system/inclrtl" include "system/inclrtl"
type type
TCharSet* {.deprecated.} = set[char] # for compatibility with Nim CharSet* {.deprecated.} = set[char] # for compatibility with Nim
{.deprecated: [TCharSet: CharSet].}
const const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}

View file

@ -37,13 +37,14 @@ proc raiseInvalidFormat(msg: string) {.noinline.} =
raise newException(SubexError, "invalid format string: " & msg) raise newException(SubexError, "invalid format string: " & msg)
type type
TFormatParser = object {.pure, final.} FormatParser = object {.pure, final.}
when defined(js): when defined(js):
f: string # we rely on the '\0' terminator f: string # we rely on the '\0' terminator
# which JS's native string doesn't have # which JS's native string doesn't have
else: else:
f: cstring f: cstring
num, i, lineLen: int num, i, lineLen: int
{.deprecated: [TFormatParser: FormatParser].}
template call(x: stmt) {.immediate.} = template call(x: stmt) {.immediate.} =
p.i = i p.i = i
@ -57,7 +58,7 @@ template callNoLineLenTracking(x: stmt) {.immediate.} =
i = p.i i = p.i
p.lineLen = oldLineLen p.lineLen = oldLineLen
proc getFormatArg(p: var TFormatParser, a: openArray[string]): int = proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'} const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
var i = p.i var i = p.i
var f = p.f var f = p.f
@ -90,22 +91,22 @@ proc getFormatArg(p: var TFormatParser, a: openArray[string]): int =
if result >=% a.len: raiseInvalidFormat("index out of bounds: " & $result) if result >=% a.len: raiseInvalidFormat("index out of bounds: " & $result)
p.i = i p.i = i
proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) {. proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) {.
noSideEffect.} noSideEffect.}
proc emitChar(p: var TFormatParser, x: var string, ch: char) {.inline.} = proc emitChar(p: var FormatParser, x: var string, ch: char) {.inline.} =
x.add(ch) x.add(ch)
if ch == '\L': p.lineLen = 0 if ch == '\L': p.lineLen = 0
else: inc p.lineLen else: inc p.lineLen
proc emitStrLinear(p: var TFormatParser, x: var string, y: string) {.inline.} = proc emitStrLinear(p: var FormatParser, x: var string, y: string) {.inline.} =
for ch in items(y): emitChar(p, x, ch) for ch in items(y): emitChar(p, x, ch)
proc emitStr(p: var TFormatParser, x: var string, y: string) {.inline.} = proc emitStr(p: var FormatParser, x: var string, y: string) {.inline.} =
x.add(y) x.add(y)
inc p.lineLen, y.len inc p.lineLen, y.len
proc scanQuote(p: var TFormatParser, x: var string, toAdd: bool) = proc scanQuote(p: var FormatParser, x: var string, toAdd: bool) =
var i = p.i+1 var i = p.i+1
var f = p.f var f = p.f
while true: while true:
@ -120,7 +121,7 @@ proc scanQuote(p: var TFormatParser, x: var string, toAdd: bool) =
inc i inc i
p.i = i p.i = i
proc scanBranch(p: var TFormatParser, a: openArray[string], proc scanBranch(p: var FormatParser, a: openArray[string],
x: var string, choice: int) = x: var string, choice: int) =
var i = p.i var i = p.i
var f = p.f var f = p.f
@ -167,7 +168,7 @@ proc scanBranch(p: var TFormatParser, a: openArray[string],
i = last i = last
p.i = i+1 p.i = i+1
proc scanSlice(p: var TFormatParser, a: openarray[string]): tuple[x, y: int] = proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
var slice = false var slice = false
var i = p.i var i = p.i
var f = p.f var f = p.f
@ -193,7 +194,7 @@ proc scanSlice(p: var TFormatParser, a: openarray[string]): tuple[x, y: int] =
inc i inc i
p.i = i p.i = i
proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) = proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
var i = p.i var i = p.i
var f = p.f var f = p.f
case f[i] case f[i]
@ -312,7 +313,7 @@ proc subex*(s: string): Subex =
proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {. proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
noSideEffect, rtl, extern: "nfrmtAddf".} = noSideEffect, rtl, extern: "nfrmtAddf".} =
## The same as ``add(s, formatstr % a)``, but more efficient. ## The same as ``add(s, formatstr % a)``, but more efficient.
var p: TFormatParser var p: FormatParser
p.f = formatstr.string p.f = formatstr.string
var i = 0 var i = 0
while i < len(formatstr.string): while i < len(formatstr.string):
@ -386,10 +387,10 @@ when isMainModule:
longishA, longishA,
longish)""" longish)"""
assert "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA", assert "type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] == "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] ==
strutils.unindent """ strutils.unindent """
type TMyEnum* = enum type MyEnum* = enum
fieldA, fieldB, fieldA, fieldB,
FiledClkad, fieldD, FiledClkad, fieldD,
fieldE, longishFieldName""" fieldE, longishFieldName"""
@ -400,11 +401,11 @@ when isMainModule:
doAssert subex"$['''|'|''''|']']#" % "0" == "'|" doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
assert subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [ assert subex("type\n Enum = enum\n $', '40c'\n '{..}") % [
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] == "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] ==
strutils.unindent """ strutils.unindent """
type type
TEnum = enum Enum = enum
fieldNameA, fieldNameB, fieldNameC, fieldNameA, fieldNameB, fieldNameC,
fieldNameD""" fieldNameD"""

View file

@ -20,7 +20,7 @@ when defined(windows):
import windows, os import windows, os
var var
conHandle: THandle conHandle: Handle
# = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0)
block: block:
@ -30,13 +30,13 @@ when defined(windows):
raiseOSError(osLastError()) raiseOSError(osLastError())
proc getCursorPos(): tuple [x,y: int] = proc getCursorPos(): tuple [x,y: int] =
var c: TCONSOLESCREENBUFFERINFO var c: CONSOLESCREENBUFFERINFO
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y)) return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y))
proc getAttributes(): int16 = proc getAttributes(): int16 =
var c: TCONSOLESCREENBUFFERINFO var c: CONSOLESCREENBUFFERINFO
# workaround Windows bugs: try several times # workaround Windows bugs: try several times
if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0: if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
return c.wAttributes return c.wAttributes
@ -51,11 +51,11 @@ else:
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) = proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
var mode: Termios var mode: Termios
discard fd.tcgetattr(addr mode) discard fd.tcgetattr(addr mode)
mode.c_iflag = mode.c_iflag and not Tcflag(BRKINT or ICRNL or INPCK or mode.c_iflag = mode.c_iflag and not Cflag(BRKINT or ICRNL or INPCK or
ISTRIP or IXON) ISTRIP or IXON)
mode.c_oflag = mode.c_oflag and not Tcflag(OPOST) mode.c_oflag = mode.c_oflag and not Cflag(OPOST)
mode.c_cflag = (mode.c_cflag and not Tcflag(CSIZE or PARENB)) or CS8 mode.c_cflag = (mode.c_cflag and not Cflag(CSIZE or PARENB)) or CS8
mode.c_lflag = mode.c_lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG) mode.c_lflag = mode.c_lflag and not Cflag(ECHO or ICANON or IEXTEN or ISIG)
mode.c_cc[VMIN] = 1.cuchar mode.c_cc[VMIN] = 1.cuchar
mode.c_cc[VTIME] = 0.cuchar mode.c_cc[VTIME] = 0.cuchar
discard fd.tcsetattr(time, addr mode) discard fd.tcsetattr(time, addr mode)
@ -64,7 +64,7 @@ proc setCursorPos*(x, y: int) =
## sets the terminal's cursor to the (x,y) position. (0,0) is the ## sets the terminal's cursor to the (x,y) position. (0,0) is the
## upper left of the screen. ## upper left of the screen.
when defined(windows): when defined(windows):
var c: TCOORD var c: COORD
c.X = int16(x) c.X = int16(x)
c.Y = int16(y) c.Y = int16(y)
if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError()) if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
@ -75,7 +75,7 @@ proc setCursorXPos*(x: int) =
## sets the terminal's cursor to the x position. The y position is ## sets the terminal's cursor to the x position. The y position is
## not changed. ## not changed.
when defined(windows): when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -91,7 +91,7 @@ when defined(windows):
## sets the terminal's cursor to the y position. The x position is ## sets the terminal's cursor to the y position. The x position is
## not changed. **Warning**: This is not supported on UNIX! ## not changed. **Warning**: This is not supported on UNIX!
when defined(windows): when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -172,7 +172,7 @@ else:
proc eraseLine* = proc eraseLine* =
## Erases the entire current line. ## Erases the entire current line.
when defined(windows): when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var numwrote: DWORD var numwrote: DWORD
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
@ -196,9 +196,9 @@ proc eraseLine* =
proc eraseScreen* = proc eraseScreen* =
## Erases the screen with the background colour and moves the cursor to home. ## Erases the screen with the background colour and moves the cursor to home.
when defined(windows): when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var numwrote: DWORD var numwrote: DWORD
var origin: TCOORD # is inititalized to 0, 0 var origin: COORD # is inititalized to 0, 0
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:

View file

@ -470,7 +470,7 @@ when not defined(JS):
posix_gettimeofday(a) posix_gettimeofday(a)
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001 result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
elif defined(windows): elif defined(windows):
var f: winlean.TFILETIME var f: winlean.FILETIME
getSystemTimeAsFileTime(f) getSystemTimeAsFileTime(f)
var i64 = rdFileTime(f) - epochDiff var i64 = rdFileTime(f) - epochDiff
var secs = i64 div rateDiff var secs = i64 div rateDiff

View file

@ -249,7 +249,7 @@ when defined(nimNewShared):
guarded* {.magic: "Guarded".} guarded* {.magic: "Guarded".}
# comparison operators: # comparison operators:
proc `==` *[TEnum: enum](x, y: TEnum): bool {.magic: "EqEnum", noSideEffect.} proc `==` *[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.}
proc `==` *(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} proc `==` *(x, y: pointer): bool {.magic: "EqRef", noSideEffect.}
proc `==` *(x, y: string): bool {.magic: "EqStr", noSideEffect.} proc `==` *(x, y: string): bool {.magic: "EqStr", noSideEffect.}
proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect.} proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect.}
@ -260,7 +260,7 @@ proc `==` *[T](x, y: ref T): bool {.magic: "EqRef", noSideEffect.}
proc `==` *[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.} proc `==` *[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.}
proc `==` *[T: proc](x, y: T): bool {.magic: "EqProc", noSideEffect.} proc `==` *[T: proc](x, y: T): bool {.magic: "EqProc", noSideEffect.}
proc `<=` *[TEnum: enum](x, y: TEnum): bool {.magic: "LeEnum", noSideEffect.} proc `<=` *[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.}
proc `<=` *(x, y: string): bool {.magic: "LeStr", noSideEffect.} proc `<=` *(x, y: string): bool {.magic: "LeStr", noSideEffect.}
proc `<=` *(x, y: char): bool {.magic: "LeCh", noSideEffect.} proc `<=` *(x, y: char): bool {.magic: "LeCh", noSideEffect.}
proc `<=` *[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.} proc `<=` *[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.}
@ -268,7 +268,7 @@ proc `<=` *(x, y: bool): bool {.magic: "LeB", noSideEffect.}
proc `<=` *[T](x, y: ref T): bool {.magic: "LePtr", noSideEffect.} proc `<=` *[T](x, y: ref T): bool {.magic: "LePtr", noSideEffect.}
proc `<=` *(x, y: pointer): bool {.magic: "LePtr", noSideEffect.} proc `<=` *(x, y: pointer): bool {.magic: "LePtr", noSideEffect.}
proc `<` *[TEnum: enum](x, y: TEnum): bool {.magic: "LtEnum", noSideEffect.} proc `<` *[Enum: enum](x, y: Enum): bool {.magic: "LtEnum", noSideEffect.}
proc `<` *(x, y: string): bool {.magic: "LtStr", noSideEffect.} proc `<` *(x, y: string): bool {.magic: "LtStr", noSideEffect.}
proc `<` *(x, y: char): bool {.magic: "LtCh", noSideEffect.} proc `<` *(x, y: char): bool {.magic: "LtCh", noSideEffect.}
proc `<` *[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.} proc `<` *[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.}
@ -332,7 +332,7 @@ type
RootObj* {.exportc: "TNimObject", inheritable.} = RootObj* {.exportc: "TNimObject", inheritable.} =
object ## the root of Nim's object hierarchy. Objects should object ## the root of Nim's object hierarchy. Objects should
## inherit from TObject or one of its descendants. However, ## inherit from RootObj or one of its descendants. However,
## objects that have no ancestor are allowed. ## objects that have no ancestor are allowed.
RootRef* = ref RootObj ## reference to RootObj RootRef* = ref RootObj ## reference to RootObj
@ -1505,7 +1505,7 @@ proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
## as it is. This operator is useful for generic code, so ## as it is. This operator is useful for generic code, so
## that ``$expr`` also works if ``expr`` is already a string. ## that ``$expr`` also works if ``expr`` is already a string.
proc `$` *[TEnum: enum](x: TEnum): string {.magic: "EnumToStr", noSideEffect.} proc `$` *[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
## The stringify operator for an enumeration argument. This works for ## The stringify operator for an enumeration argument. This works for
## any enumeration type thanks to compiler magic. If ## any enumeration type thanks to compiler magic. If
## a ``$`` operator for a concrete enumeration is provided, this is ## a ``$`` operator for a concrete enumeration is provided, this is
@ -1578,7 +1578,7 @@ else:
type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum
iterator countdown*[T](a, b: T, step = 1): T {.inline.} = iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
## Counts from ordinal value `a` down to `b` with the given ## Counts from ordinal value `a` down to `b` (inclusive) with the given
## step count. `T` may be any ordinal type, `step` may only ## step count. `T` may be any ordinal type, `step` may only
## be positive. **Note**: This fails to count to ``low(int)`` if T = int for ## be positive. **Note**: This fails to count to ``low(int)`` if T = int for
## efficiency reasons. ## efficiency reasons.
@ -1606,7 +1606,7 @@ template countupImpl(incr: stmt) {.immediate, dirty.} =
incr incr
iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
## Counts from ordinal value `a` up to `b` with the given ## Counts from ordinal value `a` up to `b` (inclusive) with the given
## step count. `S`, `T` may be any ordinal type, `step` may only ## step count. `S`, `T` may be any ordinal type, `step` may only
## be positive. **Note**: This fails to count to ``high(int)`` if T = int for ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
## efficiency reasons. ## efficiency reasons.
@ -2213,6 +2213,7 @@ type
filename*: cstring ## filename of the proc that is currently executing filename*: cstring ## filename of the proc that is currently executing
len*: int16 ## length of the inspectable slots len*: int16 ## length of the inspectable slots
calldepth*: int16 ## used for max call depth checking calldepth*: int16 ## used for max call depth checking
#{.deprecated: [TFrame: Frame].}
when defined(JS): when defined(JS):
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
@ -2414,7 +2415,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc open*(f: var File, filehandle: FileHandle, proc open*(f: var File, filehandle: FileHandle,
mode: FileMode = fmRead): bool {.tags: [], benign.} mode: FileMode = fmRead): bool {.tags: [], benign.}
## Creates a ``TFile`` from a `filehandle` with given `mode`. ## Creates a ``File`` from a `filehandle` with given `mode`.
## ##
## Default mode is readonly. Returns true iff the file could be opened. ## Default mode is readonly. Returns true iff the file could be opened.
@ -2604,6 +2605,8 @@ when not defined(JS): #and not defined(NimrodVM):
context: C_JmpBuf context: C_JmpBuf
hasRaiseAction: bool hasRaiseAction: bool
raiseAction: proc (e: ref Exception): bool {.closure.} raiseAction: proc (e: ref Exception): bool {.closure.}
SafePoint = TSafePoint
# {.deprecated: [TSafePoint: SafePoint].}
when declared(initAllocator): when declared(initAllocator):
initAllocator() initAllocator()
@ -3054,9 +3057,10 @@ proc raiseAssert*(msg: string) {.noinline.} =
proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
# trick the compiler to not list ``AssertionError`` when called # trick the compiler to not list ``AssertionError`` when called
# by ``assert``. # by ``assert``.
type THide = proc (msg: string) {.noinline, raises: [], noSideEffect, type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect,
tags: [].} tags: [].}
THide(raiseAssert)(msg) {.deprecated: [THide: Hide].}
Hide(raiseAssert)(msg)
template assert*(cond: bool, msg = "") = template assert*(cond: bool, msg = "") =
## Raises ``AssertionError`` with `msg` if `cond` is false. Note ## Raises ``AssertionError`` with `msg` if `cond` is false. Note

View file

@ -98,46 +98,49 @@ const
SmallChunkSize = PageSize SmallChunkSize = PageSize
type type
PTrunk = ptr TTrunk PTrunk = ptr Trunk
TTrunk {.final.} = object Trunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0 key: int # start address at bit 0
bits: array[0..IntsPerTrunk-1, int] # a bit vector bits: array[0..IntsPerTrunk-1, int] # a bit vector
TTrunkBuckets = array[0..255, PTrunk] TrunkBuckets = array[0..255, PTrunk]
TIntSet {.final.} = object IntSet {.final.} = object
data: TTrunkBuckets data: TrunkBuckets
{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkBuckets: TrunkBuckets].}
type type
TAlignType = BiggestFloat AlignType = BiggestFloat
TFreeCell {.final, pure.} = object FreeCell {.final, pure.} = object
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount) next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
zeroField: int # 0 means cell is not used (overlaid with typ field) zeroField: int # 0 means cell is not used (overlaid with typ field)
# 1 means cell is manually managed pointer # 1 means cell is manually managed pointer
# otherwise a PNimType is stored in there # otherwise a PNimType is stored in there
PChunk = ptr TBaseChunk PChunk = ptr BaseChunk
PBigChunk = ptr TBigChunk PBigChunk = ptr BigChunk
PSmallChunk = ptr TSmallChunk PSmallChunk = ptr SmallChunk
TBaseChunk {.pure, inheritable.} = object BaseChunk {.pure, inheritable.} = object
prevSize: int # size of previous chunk; for coalescing prevSize: int # size of previous chunk; for coalescing
size: int # if < PageSize it is a small chunk size: int # if < PageSize it is a small chunk
used: bool # later will be optimized into prevSize... used: bool # later will be optimized into prevSize...
TSmallChunk = object of TBaseChunk SmallChunk = object of BaseChunk
next, prev: PSmallChunk # chunks of the same size next, prev: PSmallChunk # chunks of the same size
freeList: ptr TFreeCell freeList: ptr FreeCell
free: int # how many bytes remain free: int # how many bytes remain
acc: int # accumulator for small object allocation acc: int # accumulator for small object allocation
data: TAlignType # start of usable memory data: AlignType # start of usable memory
TBigChunk = object of TBaseChunk # not necessarily > PageSize! BigChunk = object of BaseChunk # not necessarily > PageSize!
next, prev: PBigChunk # chunks of the same (or bigger) size next, prev: PBigChunk # chunks of the same (or bigger) size
align: int align: int
data: TAlignType # start of usable memory data: AlignType # start of usable memory
{.deprecated: [TAlignType: AlignType, TFreeCell: FreeCell, TBaseChunk: BaseChunk,
TBigChunk: BigChunk, TSmallChunk: SmallChunk].}
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType) template smallChunkOverhead(): expr = sizeof(SmallChunk)-sizeof(AlignType)
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType) template bigChunkOverhead(): expr = sizeof(BigChunk)-sizeof(AlignType)
proc roundup(x, v: int): int {.inline.} = proc roundup(x, v: int): int {.inline.} =
result = (x + (v-1)) and not (v-1) result = (x + (v-1)) and not (v-1)
@ -156,31 +159,32 @@ sysAssert(roundup(65, 8) == 72, "roundup broken 2")
# to the OS), a fixed size array can be used. # to the OS), a fixed size array can be used.
type type
PLLChunk = ptr TLLChunk PLLChunk = ptr LLChunk
TLLChunk {.pure.} = object ## *low-level* chunk LLChunk {.pure.} = object ## *low-level* chunk
size: int # remaining size size: int # remaining size
acc: int # accumulator acc: int # accumulator
next: PLLChunk # next low-level chunk; only needed for dealloc next: PLLChunk # next low-level chunk; only needed for dealloc
PAvlNode = ptr TAvlNode PAvlNode = ptr AvlNode
TAvlNode {.pure, final.} = object AvlNode {.pure, final.} = object
link: array[0..1, PAvlNode] # Left (0) and right (1) links link: array[0..1, PAvlNode] # Left (0) and right (1) links
key, upperBound: int key, upperBound: int
level: int level: int
TMemRegion {.final, pure.} = object MemRegion {.final, pure.} = object
minLargeObj, maxLargeObj: int minLargeObj, maxLargeObj: int
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk] freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
llmem: PLLChunk llmem: PLLChunk
currMem, maxMem, freeMem: int # memory sizes (allocated from OS) currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
lastSize: int # needed for the case that OS gives us pages linearly lastSize: int # needed for the case that OS gives us pages linearly
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
chunkStarts: TIntSet chunkStarts: IntSet
root, deleted, last, freeAvlNodes: PAvlNode root, deleted, last, freeAvlNodes: PAvlNode
{.deprecated: [TLLChunk: LLChunk, TAvlNode: AvlNode, TMemRegion: MemRegion].}
# shared: # shared:
var var
bottomData: TAvlNode bottomData: AvlNode
bottom: PAvlNode bottom: PAvlNode
{.push stack_trace: off.} {.push stack_trace: off.}
@ -191,44 +195,44 @@ proc initAllocator() =
bottom.link[1] = bottom bottom.link[1] = bottom
{.pop.} {.pop.}
proc incCurrMem(a: var TMemRegion, bytes: int) {.inline.} = proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} =
inc(a.currMem, bytes) inc(a.currMem, bytes)
proc decCurrMem(a: var TMemRegion, bytes: int) {.inline.} = proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} =
a.maxMem = max(a.maxMem, a.currMem) a.maxMem = max(a.maxMem, a.currMem)
dec(a.currMem, bytes) dec(a.currMem, bytes)
proc getMaxMem(a: var TMemRegion): int = proc getMaxMem(a: var MemRegion): int =
# Since we update maxPagesCount only when freeing pages, # Since we update maxPagesCount only when freeing pages,
# maxPagesCount may not be up to date. Thus we use the # maxPagesCount may not be up to date. Thus we use the
# maximum of these both values here: # maximum of these both values here:
result = max(a.currMem, a.maxMem) result = max(a.currMem, a.maxMem)
proc llAlloc(a: var TMemRegion, size: int): pointer = proc llAlloc(a: var MemRegion, size: int): pointer =
# *low-level* alloc for the memory managers data structures. Deallocation # *low-level* alloc for the memory managers data structures. Deallocation
# is done at he end of the allocator's life time. # is done at he end of the allocator's life time.
if a.llmem == nil or size > a.llmem.size: if a.llmem == nil or size > a.llmem.size:
# the requested size is ``roundup(size+sizeof(TLLChunk), PageSize)``, but # the requested size is ``roundup(size+sizeof(LLChunk), PageSize)``, but
# since we know ``size`` is a (small) constant, we know the requested size # since we know ``size`` is a (small) constant, we know the requested size
# is one page: # is one page:
sysAssert roundup(size+sizeof(TLLChunk), PageSize) == PageSize, "roundup 6" sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6"
var old = a.llmem # can be nil and is correct with nil var old = a.llmem # can be nil and is correct with nil
a.llmem = cast[PLLChunk](osAllocPages(PageSize)) a.llmem = cast[PLLChunk](osAllocPages(PageSize))
incCurrMem(a, PageSize) incCurrMem(a, PageSize)
a.llmem.size = PageSize - sizeof(TLLChunk) a.llmem.size = PageSize - sizeof(LLChunk)
a.llmem.acc = sizeof(TLLChunk) a.llmem.acc = sizeof(LLChunk)
a.llmem.next = old a.llmem.next = old
result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc) result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc)
dec(a.llmem.size, size) dec(a.llmem.size, size)
inc(a.llmem.acc, size) inc(a.llmem.acc, size)
zeroMem(result, size) zeroMem(result, size)
proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode = proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
if a.freeAvlNodes != nil: if a.freeAvlNodes != nil:
result = a.freeAvlNodes result = a.freeAvlNodes
a.freeAvlNodes = a.freeAvlNodes.link[0] a.freeAvlNodes = a.freeAvlNodes.link[0]
else: else:
result = cast[PAvlNode](llAlloc(a, sizeof(TAvlNode))) result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
result.key = key result.key = key
result.upperBound = upperBound result.upperBound = upperBound
result.link[0] = bottom result.link[0] = bottom
@ -238,13 +242,13 @@ proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode =
sysAssert(bottom.link[0] == bottom, "bottom link[0]") sysAssert(bottom.link[0] == bottom, "bottom link[0]")
sysAssert(bottom.link[1] == bottom, "bottom link[1]") sysAssert(bottom.link[1] == bottom, "bottom link[1]")
proc deallocAvlNode(a: var TMemRegion, n: PAvlNode) {.inline.} = proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
n.link[0] = a.freeAvlNodes n.link[0] = a.freeAvlNodes
a.freeAvlNodes = n a.freeAvlNodes = n
include "system/avltree" include "system/avltree"
proc llDeallocAll(a: var TMemRegion) = proc llDeallocAll(a: var MemRegion) =
var it = a.llmem var it = a.llmem
while it != nil: while it != nil:
# we know each block in the list has the size of 1 page: # we know each block in the list has the size of 1 page:
@ -252,14 +256,14 @@ proc llDeallocAll(a: var TMemRegion) =
osDeallocPages(it, PageSize) osDeallocPages(it, PageSize)
it = next it = next
proc intSetGet(t: TIntSet, key: int): PTrunk = proc intSetGet(t: IntSet, key: int): PTrunk =
var it = t.data[key and high(t.data)] var it = t.data[key and high(t.data)]
while it != nil: while it != nil:
if it.key == key: return it if it.key == key: return it
it = it.next it = it.next
result = nil result = nil
proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk = proc intSetPut(a: var MemRegion, t: var IntSet, key: int): PTrunk =
result = intSetGet(t, key) result = intSetGet(t, key)
if result == nil: if result == nil:
result = cast[PTrunk](llAlloc(a, sizeof(result[]))) result = cast[PTrunk](llAlloc(a, sizeof(result[])))
@ -267,7 +271,7 @@ proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk =
t.data[key and high(t.data)] = result t.data[key and high(t.data)] = result
result.key = key result.key = key
proc contains(s: TIntSet, key: int): bool = proc contains(s: IntSet, key: int): bool =
var t = intSetGet(s, key shr TrunkShift) var t = intSetGet(s, key shr TrunkShift)
if t != nil: if t != nil:
var u = key and TrunkMask var u = key and TrunkMask
@ -275,19 +279,19 @@ proc contains(s: TIntSet, key: int): bool =
else: else:
result = false result = false
proc incl(a: var TMemRegion, s: var TIntSet, key: int) = proc incl(a: var MemRegion, s: var IntSet, key: int) =
var t = intSetPut(a, s, key shr TrunkShift) var t = intSetPut(a, s, key shr TrunkShift)
var u = key and TrunkMask var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask)) t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
proc excl(s: var TIntSet, key: int) = proc excl(s: var IntSet, key: int) =
var t = intSetGet(s, key shr TrunkShift) var t = intSetGet(s, key shr TrunkShift)
if t != nil: if t != nil:
var u = key and TrunkMask var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
(1 shl (u and IntMask)) (1 shl (u and IntMask))
iterator elements(t: TIntSet): int {.inline.} = iterator elements(t: IntSet): int {.inline.} =
# while traversing it is forbidden to change the set! # while traversing it is forbidden to change the set!
for h in 0..high(t.data): for h in 0..high(t.data):
var r = t.data[h] var r = t.data[h]
@ -311,7 +315,7 @@ proc isSmallChunk(c: PChunk): bool {.inline.} =
proc chunkUnused(c: PChunk): bool {.inline.} = proc chunkUnused(c: PChunk): bool {.inline.} =
result = not c.used result = not c.used
iterator allObjects(m: TMemRegion): pointer {.inline.} = iterator allObjects(m: MemRegion): pointer {.inline.} =
for s in elements(m.chunkStarts): for s in elements(m.chunkStarts):
# we need to check here again as it could have been modified: # we need to check here again as it could have been modified:
if s in m.chunkStarts: if s in m.chunkStarts:
@ -331,7 +335,7 @@ iterator allObjects(m: TMemRegion): pointer {.inline.} =
yield addr(c.data) yield addr(c.data)
proc isCell(p: pointer): bool {.inline.} = proc isCell(p: pointer): bool {.inline.} =
result = cast[ptr TFreeCell](p).zeroField >% 1 result = cast[ptr FreeCell](p).zeroField >% 1
# ------------- chunk management ---------------------------------------------- # ------------- chunk management ----------------------------------------------
proc pageIndex(c: PChunk): int {.inline.} = proc pageIndex(c: PChunk): int {.inline.} =
@ -344,7 +348,7 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
result = cast[PChunk](cast[ByteAddress](p) and not PageMask) result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
#sysAssert(Contains(allocator.chunkStarts, pageIndex(result))) #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
incCurrMem(a, size) incCurrMem(a, size)
inc(a.freeMem, size) inc(a.freeMem, size)
result = cast[PBigChunk](osAllocPages(size)) result = cast[PBigChunk](osAllocPages(size))
@ -373,7 +377,7 @@ proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk =
result.prevSize = 0 # unknown result.prevSize = 0 # unknown
a.lastSize = size # for next request a.lastSize = size # for next request
proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) = proc freeOsChunks(a: var MemRegion, p: pointer, size: int) =
# update next.prevSize: # update next.prevSize:
var c = cast[PChunk](p) var c = cast[PChunk](p)
var nxt = cast[ByteAddress](p) +% c.size var nxt = cast[ByteAddress](p) +% c.size
@ -387,7 +391,7 @@ proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) =
dec(a.freeMem, size) dec(a.freeMem, size)
#c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size) #c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size)
proc isAccessible(a: TMemRegion, p: pointer): bool {.inline.} = proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
result = contains(a.chunkStarts, pageIndex(p)) result = contains(a.chunkStarts, pageIndex(p))
proc contains[T](list, x: T): bool = proc contains[T](list, x: T): bool =
@ -396,7 +400,7 @@ proc contains[T](list, x: T): bool =
if it == x: return true if it == x: return true
it = it.next it = it.next
proc writeFreeList(a: TMemRegion) = proc writeFreeList(a: MemRegion) =
var it = a.freeChunksList var it = a.freeChunksList
c_fprintf(c_stdout, "freeChunksList: %p\n", it) c_fprintf(c_stdout, "freeChunksList: %p\n", it)
while it != nil: while it != nil:
@ -427,14 +431,14 @@ proc listRemove[T](head: var T, c: T) {.inline.} =
c.next = nil c.next = nil
c.prev = nil c.prev = nil
proc updatePrevSize(a: var TMemRegion, c: PBigChunk, proc updatePrevSize(a: var MemRegion, c: PBigChunk,
prevSize: int) {.inline.} = prevSize: int) {.inline.} =
var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize") sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize")
if isAccessible(a, ri): if isAccessible(a, ri):
ri.prevSize = prevSize ri.prevSize = prevSize
proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
var c = c var c = c
sysAssert(c.size >= PageSize, "freeBigChunk") sysAssert(c.size >= PageSize, "freeBigChunk")
inc(a.freeMem, c.size) inc(a.freeMem, c.size)
@ -467,7 +471,7 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) =
else: else:
freeOsChunks(a, c, c.size) freeOsChunks(a, c, c.size)
proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) = proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
var rest = cast[PBigChunk](cast[ByteAddress](c) +% size) var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
sysAssert(rest notin a.freeChunksList, "splitChunk") sysAssert(rest notin a.freeChunksList, "splitChunk")
rest.size = c.size - size rest.size = c.size - size
@ -480,7 +484,7 @@ proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) =
incl(a, a.chunkStarts, pageIndex(rest)) incl(a, a.chunkStarts, pageIndex(rest))
listAdd(a.freeChunksList, rest) listAdd(a.freeChunksList, rest)
proc getBigChunk(a: var TMemRegion, size: int): PBigChunk = proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
# use first fit for now: # use first fit for now:
sysAssert((size and PageMask) == 0, "getBigChunk 1") sysAssert((size and PageMask) == 0, "getBigChunk 1")
sysAssert(size > 0, "getBigChunk 2") sysAssert(size > 0, "getBigChunk 2")
@ -507,16 +511,16 @@ proc getBigChunk(a: var TMemRegion, size: int): PBigChunk =
incl(a, a.chunkStarts, pageIndex(result)) incl(a, a.chunkStarts, pageIndex(result))
dec(a.freeMem, size) dec(a.freeMem, size)
proc getSmallChunk(a: var TMemRegion): PSmallChunk = proc getSmallChunk(a: var MemRegion): PSmallChunk =
var res = getBigChunk(a, PageSize) var res = getBigChunk(a, PageSize)
sysAssert res.prev == nil, "getSmallChunk 1" sysAssert res.prev == nil, "getSmallChunk 1"
sysAssert res.next == nil, "getSmallChunk 2" sysAssert res.next == nil, "getSmallChunk 2"
result = cast[PSmallChunk](res) result = cast[PSmallChunk](res)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool {.benign.} proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
proc allocInv(a: TMemRegion): bool = proc allocInv(a: MemRegion): bool =
## checks some (not all yet) invariants of the allocator's data structures. ## checks some (not all yet) invariants of the allocator's data structures.
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks): for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
var c = a.freeSmallChunks[s] var c = a.freeSmallChunks[s]
@ -537,10 +541,10 @@ proc allocInv(a: TMemRegion): bool =
c = c.next c = c.next
result = true result = true
proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: begin") sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
sysAssert(requestedSize >= sizeof(TFreeCell), "rawAlloc: requested size too small") sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
var size = roundup(requestedSize, MemAlign) var size = roundup(requestedSize, MemAlign)
sysAssert(size >= requestedSize, "insufficient allocated size!") sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size) #c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
@ -601,11 +605,11 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: end") sysAssert(allocInv(a), "rawAlloc: end")
when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result) when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result)
proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer = proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
result = rawAlloc(a, requestedSize) result = rawAlloc(a, requestedSize)
zeroMem(result, requestedSize) zeroMem(result, requestedSize)
proc rawDealloc(a: var TMemRegion, p: pointer) = proc rawDealloc(a: var MemRegion, p: pointer) =
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer") #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
sysAssert(allocInv(a), "rawDealloc: begin") sysAssert(allocInv(a), "rawDealloc: begin")
var c = pageAddr(p) var c = pageAddr(p)
@ -615,7 +619,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
var s = c.size var s = c.size
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 3") s == 0, "rawDealloc 3")
var f = cast[ptr TFreeCell](p) var f = cast[ptr FreeCell](p)
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField))) #echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
sysAssert(f.zeroField != 0, "rawDealloc 1") sysAssert(f.zeroField != 0, "rawDealloc 1")
f.zeroField = 0 f.zeroField = 0
@ -623,8 +627,8 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
c.freeList = f c.freeList = f
when overwriteFree: when overwriteFree:
# set to 0xff to check for usage after free bugs: # set to 0xff to check for usage after free bugs:
c_memset(cast[pointer](cast[int](p) +% sizeof(TFreeCell)), -1'i32, c_memset(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
s -% sizeof(TFreeCell)) s -% sizeof(FreeCell))
# check if it is not in the freeSmallChunks[s] list: # check if it is not in the freeSmallChunks[s] list:
if c.free < s: if c.free < s:
# add it to the freeSmallChunks[s] array: # add it to the freeSmallChunks[s] array:
@ -649,7 +653,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
sysAssert(allocInv(a), "rawDealloc: end") sysAssert(allocInv(a), "rawDealloc: end")
when logAlloc: cprintf("rawDealloc: %p\n", p) when logAlloc: cprintf("rawDealloc: %p\n", p)
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
if isAccessible(a, p): if isAccessible(a, p):
var c = pageAddr(p) var c = pageAddr(p)
if not chunkUnused(c): if not chunkUnused(c):
@ -658,16 +662,16 @@ proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
var offset = (cast[ByteAddress](p) and (PageSize-1)) -% var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
smallChunkOverhead() smallChunkOverhead()
result = (c.acc >% offset) and (offset %% c.size == 0) and result = (c.acc >% offset) and (offset %% c.size == 0) and
(cast[ptr TFreeCell](p).zeroField >% 1) (cast[ptr FreeCell](p).zeroField >% 1)
else: else:
var c = cast[PBigChunk](c) var c = cast[PBigChunk](c)
result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1 result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
proc prepareForInteriorPointerChecking(a: var TMemRegion) {.inline.} = proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
a.minLargeObj = lowGauge(a.root) a.minLargeObj = lowGauge(a.root)
a.maxLargeObj = highGauge(a.root) a.maxLargeObj = highGauge(a.root)
proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer = proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
if isAccessible(a, p): if isAccessible(a, p):
var c = pageAddr(p) var c = pageAddr(p)
if not chunkUnused(c): if not chunkUnused(c):
@ -678,7 +682,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
if c.acc >% offset: if c.acc >% offset:
sysAssert(cast[ByteAddress](addr(c.data)) +% offset == sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
cast[ByteAddress](p), "offset is not what you think it is") cast[ByteAddress](p), "offset is not what you think it is")
var d = cast[ptr TFreeCell](cast[ByteAddress](addr(c.data)) +% var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
offset -% (offset %% c.size)) offset -% (offset %% c.size))
if d.zeroField >% 1: if d.zeroField >% 1:
result = d result = d
@ -686,7 +690,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
else: else:
var c = cast[PBigChunk](c) var c = cast[PBigChunk](c)
var d = addr(c.data) var d = addr(c.data)
if p >= d and cast[ptr TFreeCell](d).zeroField >% 1: if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
result = d result = d
sysAssert isAllocatedPtr(a, result), " result wrong pointer!" sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
else: else:
@ -699,38 +703,38 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
var k = cast[pointer](avlNode.key) var k = cast[pointer](avlNode.key)
var c = cast[PBigChunk](pageAddr(k)) var c = cast[PBigChunk](pageAddr(k))
sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!") sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
if cast[ptr TFreeCell](k).zeroField >% 1: if cast[ptr FreeCell](k).zeroField >% 1:
result = k result = k
sysAssert isAllocatedPtr(a, result), " result wrong pointer!" sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
proc ptrSize(p: pointer): int = proc ptrSize(p: pointer): int =
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell)) var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
var c = pageAddr(p) var c = pageAddr(p)
sysAssert(not chunkUnused(c), "ptrSize") sysAssert(not chunkUnused(c), "ptrSize")
result = c.size -% sizeof(TFreeCell) result = c.size -% sizeof(FreeCell)
if not isSmallChunk(c): if not isSmallChunk(c):
dec result, bigChunkOverhead() dec result, bigChunkOverhead()
proc alloc(allocator: var TMemRegion, size: Natural): pointer = proc alloc(allocator: var MemRegion, size: Natural): pointer =
result = rawAlloc(allocator, size+sizeof(TFreeCell)) result = rawAlloc(allocator, size+sizeof(FreeCell))
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used cast[ptr FreeCell](result).zeroField = 1 # mark it as used
sysAssert(not isAllocatedPtr(allocator, result), "alloc") sysAssert(not isAllocatedPtr(allocator, result), "alloc")
result = cast[pointer](cast[ByteAddress](result) +% sizeof(TFreeCell)) result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
proc alloc0(allocator: var TMemRegion, size: Natural): pointer = proc alloc0(allocator: var MemRegion, size: Natural): pointer =
result = alloc(allocator, size) result = alloc(allocator, size)
zeroMem(result, size) zeroMem(result, size)
proc dealloc(allocator: var TMemRegion, p: pointer) = proc dealloc(allocator: var MemRegion, p: pointer) =
sysAssert(p != nil, "dealloc 0") sysAssert(p != nil, "dealloc 0")
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell)) var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
sysAssert(x != nil, "dealloc 1") sysAssert(x != nil, "dealloc 1")
sysAssert(isAccessible(allocator, x), "is not accessible") sysAssert(isAccessible(allocator, x), "is not accessible")
sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 2") sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc 2")
rawDealloc(allocator, x) rawDealloc(allocator, x)
sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3") sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer = proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
if newsize > 0: if newsize > 0:
result = alloc0(allocator, newsize) result = alloc0(allocator, newsize)
if p != nil: if p != nil:
@ -739,7 +743,7 @@ proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer =
elif p != nil: elif p != nil:
dealloc(allocator, p) dealloc(allocator, p)
proc deallocOsPages(a: var TMemRegion) = proc deallocOsPages(a: var MemRegion) =
# we free every 'ordinarily' allocated page by iterating over the page bits: # we free every 'ordinarily' allocated page by iterating over the page bits:
for p in elements(a.chunkStarts): for p in elements(a.chunkStarts):
var page = cast[PChunk](p shl PageShift) var page = cast[PChunk](p shl PageShift)
@ -756,9 +760,9 @@ proc deallocOsPages(a: var TMemRegion) =
# And then we free the pages that are in use for the page bits: # And then we free the pages that are in use for the page bits:
llDeallocAll(a) llDeallocAll(a)
proc getFreeMem(a: TMemRegion): int {.inline.} = result = a.freeMem proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
proc getTotalMem(a: TMemRegion): int {.inline.} = result = a.currMem proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
proc getOccupiedMem(a: TMemRegion): int {.inline.} = proc getOccupiedMem(a: MemRegion): int {.inline.} =
result = a.currMem - a.freeMem result = a.currMem - a.freeMem
# ---------------------- thread memory region ------------------------------- # ---------------------- thread memory region -------------------------------
@ -769,7 +773,7 @@ template instantiateForRegion(allocator: expr) =
result = interiorAllocatedPtr(allocator, p) result = interiorAllocatedPtr(allocator, p)
proc isAllocatedPtr*(p: pointer): bool = proc isAllocatedPtr*(p: pointer): bool =
let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(TCell))) let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell)))
result = isAllocatedPtr(allocator, p) result = isAllocatedPtr(allocator, p)
proc deallocOsPages = deallocOsPages(allocator) proc deallocOsPages = deallocOsPages(allocator)
@ -803,8 +807,8 @@ template instantiateForRegion(allocator: expr) =
# -------------------- shared heap region ---------------------------------- # -------------------- shared heap region ----------------------------------
when hasThreadSupport: when hasThreadSupport:
var sharedHeap: TMemRegion var sharedHeap: MemRegion
var heapLock: TSysLock var heapLock: SysLock
initSysLock(heapLock) initSysLock(heapLock)
proc allocShared(size: Natural): pointer = proc allocShared(size: Natural): pointer =

View file

@ -37,39 +37,40 @@ when someGcc and hasThreadSupport:
## and release stores in all threads. ## and release stores in all threads.
type type
TAtomType* = SomeNumber|pointer|ptr|char|bool AtomType* = SomeNumber|pointer|ptr|char|bool
## Type Class representing valid types for use with atomic procs ## Type Class representing valid types for use with atomic procs
{.deprecated: [TAtomType: AtomType].}
proc atomicLoadN*[T: TAtomType](p: ptr T, mem: AtomMemModel): T {. proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {.
importc: "__atomic_load_n", nodecl.} importc: "__atomic_load_n", nodecl.}
## This proc implements an atomic load operation. It returns the contents at p. ## This proc implements an atomic load operation. It returns the contents at p.
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME. ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME.
proc atomicLoad*[T: TAtomType](p, ret: ptr T, mem: AtomMemModel) {. proc atomicLoad*[T: AtomType](p, ret: ptr T, mem: AtomMemModel) {.
importc: "__atomic_load", nodecl.} importc: "__atomic_load", nodecl.}
## This is the generic version of an atomic load. It returns the contents at p in ret. ## This is the generic version of an atomic load. It returns the contents at p in ret.
proc atomicStoreN*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel) {. proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel) {.
importc: "__atomic_store_n", nodecl.} importc: "__atomic_store_n", nodecl.}
## This proc implements an atomic store operation. It writes val at p. ## This proc implements an atomic store operation. It writes val at p.
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE. ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE.
proc atomicStore*[T: TAtomType](p, val: ptr T, mem: AtomMemModel) {. proc atomicStore*[T: AtomType](p, val: ptr T, mem: AtomMemModel) {.
importc: "__atomic_store", nodecl.} importc: "__atomic_store", nodecl.}
## This is the generic version of an atomic store. It stores the value of val at p ## This is the generic version of an atomic store. It stores the value of val at p
proc atomicExchangeN*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicExchangeN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_exchange_n", nodecl.} importc: "__atomic_exchange_n", nodecl.}
## This proc implements an atomic exchange operation. It writes val at p, ## This proc implements an atomic exchange operation. It writes val at p,
## and returns the previous contents at p. ## and returns the previous contents at p.
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL
proc atomicExchange*[T: TAtomType](p, val, ret: ptr T, mem: AtomMemModel) {. proc atomicExchange*[T: AtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
importc: "__atomic_exchange", nodecl.} importc: "__atomic_exchange", nodecl.}
## This is the generic version of an atomic exchange. It stores the contents at val at p. ## This is the generic version of an atomic exchange. It stores the contents at val at p.
## The original value at p is copied into ret. ## The original value at p is copied into ret.
proc atomicCompareExchangeN*[T: TAtomType](p, expected: ptr T, desired: T, proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T,
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {. weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
importc: "__atomic_compare_exchange_n ", nodecl.} importc: "__atomic_compare_exchange_n ", nodecl.}
## This proc implements an atomic compare and exchange operation. This compares the ## This proc implements an atomic compare and exchange operation. This compares the
@ -85,7 +86,7 @@ when someGcc and hasThreadSupport:
## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model ## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model
## than that specified by success_memmodel. ## than that specified by success_memmodel.
proc atomicCompareExchange*[T: TAtomType](p, expected, desired: ptr T, proc atomicCompareExchange*[T: AtomType](p, expected, desired: ptr T,
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {. weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
importc: "__atomic_compare_exchange", nodecl.} importc: "__atomic_compare_exchange", nodecl.}
## This proc implements the generic version of atomic_compare_exchange. ## This proc implements the generic version of atomic_compare_exchange.
@ -93,31 +94,31 @@ when someGcc and hasThreadSupport:
## value is also a pointer. ## value is also a pointer.
## Perform the operation return the new value, all memory models are valid ## Perform the operation return the new value, all memory models are valid
proc atomicAddFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicAddFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_add_fetch", nodecl.} importc: "__atomic_add_fetch", nodecl.}
proc atomicSubFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_sub_fetch", nodecl.} importc: "__atomic_sub_fetch", nodecl.}
proc atomicOrFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_or_fetch ", nodecl.} importc: "__atomic_or_fetch ", nodecl.}
proc atomicAndFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_and_fetch", nodecl.} importc: "__atomic_and_fetch", nodecl.}
proc atomicXorFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_xor_fetch", nodecl.} importc: "__atomic_xor_fetch", nodecl.}
proc atomicNandFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_nand_fetch ", nodecl.} importc: "__atomic_nand_fetch ", nodecl.}
## Perform the operation return the old value, all memory models are valid ## Perform the operation return the old value, all memory models are valid
proc atomicFetchAdd*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_add", nodecl.} importc: "__atomic_fetch_add", nodecl.}
proc atomicFetchSub*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicFetchSub*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_sub", nodecl.} importc: "__atomic_fetch_sub", nodecl.}
proc atomicFetchOr*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicFetchOr*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_or", nodecl.} importc: "__atomic_fetch_or", nodecl.}
proc atomicFetchAnd*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicFetchAnd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_and", nodecl.} importc: "__atomic_fetch_and", nodecl.}
proc atomicFetchXor*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicFetchXor*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_xor", nodecl.} importc: "__atomic_fetch_xor", nodecl.}
proc atomicFetchNand*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {. proc atomicFetchNand*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_nand", nodecl.} importc: "__atomic_fetch_nand", nodecl.}
proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {. proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {.

View file

@ -51,7 +51,7 @@ proc split(t: var PAvlNode) =
t.link[0] = temp t.link[0] = temp
inc t.level inc t.level
proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} = proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
if t == bottom: if t == bottom:
t = allocAvlNode(a, key, upperBound) t = allocAvlNode(a, key, upperBound)
else: else:
@ -64,7 +64,7 @@ proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
skew(t) skew(t)
split(t) split(t)
proc del(a: var TMemRegion, t: var PAvlNode, x: int) {.benign.} = proc del(a: var MemRegion, t: var PAvlNode, x: int) {.benign.} =
if t == bottom: return if t == bottom: return
a.last = t a.last = t
if x <% t.key: if x <% t.key:

View file

@ -10,10 +10,10 @@
# Efficient set of pointers for the GC (and repr) # Efficient set of pointers for the GC (and repr)
type type
TRefCount = int RefCount = int
TCell {.pure.} = object Cell {.pure.} = object
refcount: TRefCount # the refcount and some flags refcount: RefCount # the refcount and some flags
typ: PNimType typ: PNimType
when trackAllocationSource: when trackAllocationSource:
filename: cstring filename: cstring
@ -21,34 +21,35 @@ type
when useCellIds: when useCellIds:
id: int id: int
PCell = ptr TCell PCell = ptr Cell
PPageDesc = ptr TPageDesc PPageDesc = ptr PageDesc
TBitIndex = range[0..UnitsPerPage-1] BitIndex = range[0..UnitsPerPage-1]
TPageDesc {.final, pure.} = object PageDesc {.final, pure.} = object
next: PPageDesc # all nodes are connected with this pointer next: PPageDesc # all nodes are connected with this pointer
key: ByteAddress # start address at bit 0 key: ByteAddress # start address at bit 0
bits: array[TBitIndex, int] # a bit vector bits: array[BitIndex, int] # a bit vector
PPageDescArray = ptr array[0..1000_000, PPageDesc] PPageDescArray = ptr array[0..1000_000, PPageDesc]
TCellSet {.final, pure.} = object CellSet {.final, pure.} = object
counter, max: int counter, max: int
head: PPageDesc head: PPageDesc
data: PPageDescArray data: PPageDescArray
PCellArray = ptr array[0..100_000_000, PCell] PCellArray = ptr array[0..100_000_000, PCell]
TCellSeq {.final, pure.} = object CellSeq {.final, pure.} = object
len, cap: int len, cap: int
d: PCellArray d: PCellArray
{.deprecated: [TCell: Cell, TBitIndex: BitIndex, TPageDesc: PageDesc,
TRefCount: RefCount, TCellSet: CellSet, TCellSeq: CellSeq].}
# ------------------- cell seq handling --------------------------------------- # ------------------- cell seq handling ---------------------------------------
proc contains(s: TCellSeq, c: PCell): bool {.inline.} = proc contains(s: CellSeq, c: PCell): bool {.inline.} =
for i in 0 .. s.len-1: for i in 0 .. s.len-1:
if s.d[i] == c: return true if s.d[i] == c: return true
return false return false
proc add(s: var TCellSeq, c: PCell) {.inline.} = proc add(s: var CellSeq, c: PCell) {.inline.} =
if s.len >= s.cap: if s.len >= s.cap:
s.cap = s.cap * 3 div 2 s.cap = s.cap * 3 div 2
var d = cast[PCellArray](alloc(s.cap * sizeof(PCell))) var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
@ -59,12 +60,12 @@ proc add(s: var TCellSeq, c: PCell) {.inline.} =
s.d[s.len] = c s.d[s.len] = c
inc(s.len) inc(s.len)
proc init(s: var TCellSeq, cap: int = 1024) = proc init(s: var CellSeq, cap: int = 1024) =
s.len = 0 s.len = 0
s.cap = cap s.cap = cap
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell))) s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
proc deinit(s: var TCellSeq) = proc deinit(s: var CellSeq) =
dealloc(s.d) dealloc(s.d)
s.d = nil s.d = nil
s.len = 0 s.len = 0
@ -75,13 +76,13 @@ proc deinit(s: var TCellSeq) =
const const
InitCellSetSize = 1024 # must be a power of two! InitCellSetSize = 1024 # must be a power of two!
proc init(s: var TCellSet) = proc init(s: var CellSet) =
s.data = cast[PPageDescArray](alloc0(InitCellSetSize * sizeof(PPageDesc))) s.data = cast[PPageDescArray](alloc0(InitCellSetSize * sizeof(PPageDesc)))
s.max = InitCellSetSize-1 s.max = InitCellSetSize-1
s.counter = 0 s.counter = 0
s.head = nil s.head = nil
proc deinit(s: var TCellSet) = proc deinit(s: var CellSet) =
var it = s.head var it = s.head
while it != nil: while it != nil:
var n = it.next var n = it.next
@ -98,14 +99,14 @@ proc nextTry(h, maxHash: int): int {.inline.} =
# generates each int in range(maxHash) exactly once (see any text on # generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof). # random-number generation for proof).
proc cellSetGet(t: TCellSet, key: ByteAddress): PPageDesc = proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
var h = cast[int](key) and t.max var h = cast[int](key) and t.max
while t.data[h] != nil: while t.data[h] != nil:
if t.data[h].key == key: return t.data[h] if t.data[h].key == key: return t.data[h]
h = nextTry(h, t.max) h = nextTry(h, t.max)
return nil return nil
proc cellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) = proc cellSetRawInsert(t: CellSet, data: PPageDescArray, desc: PPageDesc) =
var h = cast[int](desc.key) and t.max var h = cast[int](desc.key) and t.max
while data[h] != nil: while data[h] != nil:
sysAssert(data[h] != desc, "CellSetRawInsert 1") sysAssert(data[h] != desc, "CellSetRawInsert 1")
@ -113,7 +114,7 @@ proc cellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) =
sysAssert(data[h] == nil, "CellSetRawInsert 2") sysAssert(data[h] == nil, "CellSetRawInsert 2")
data[h] = desc data[h] = desc
proc cellSetEnlarge(t: var TCellSet) = proc cellSetEnlarge(t: var CellSet) =
var oldMax = t.max var oldMax = t.max
t.max = ((t.max+1)*2)-1 t.max = ((t.max+1)*2)-1
var n = cast[PPageDescArray](alloc0((t.max + 1) * sizeof(PPageDesc))) var n = cast[PPageDescArray](alloc0((t.max + 1) * sizeof(PPageDesc)))
@ -123,7 +124,7 @@ proc cellSetEnlarge(t: var TCellSet) =
dealloc(t.data) dealloc(t.data)
t.data = n t.data = n
proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc = proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
var h = cast[int](key) and t.max var h = cast[int](key) and t.max
while true: while true:
var x = t.data[h] var x = t.data[h]
@ -138,7 +139,7 @@ proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
while t.data[h] != nil: h = nextTry(h, t.max) while t.data[h] != nil: h = nextTry(h, t.max)
sysAssert(t.data[h] == nil, "CellSetPut") sysAssert(t.data[h] == nil, "CellSetPut")
# the new page descriptor goes into result # the new page descriptor goes into result
result = cast[PPageDesc](alloc0(sizeof(TPageDesc))) result = cast[PPageDesc](alloc0(sizeof(PageDesc)))
result.next = t.head result.next = t.head
result.key = key result.key = key
t.head = result t.head = result
@ -146,7 +147,7 @@ proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
# ---------- slightly higher level procs -------------------------------------- # ---------- slightly higher level procs --------------------------------------
proc contains(s: TCellSet, cell: PCell): bool = proc contains(s: CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell) var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift) var t = cellSetGet(s, u shr PageShift)
if t != nil: if t != nil:
@ -155,13 +156,13 @@ proc contains(s: TCellSet, cell: PCell): bool =
else: else:
result = false result = false
proc incl(s: var TCellSet, cell: PCell) {.noinline.} = proc incl(s: var CellSet, cell: PCell) {.noinline.} =
var u = cast[ByteAddress](cell) var u = cast[ByteAddress](cell)
var t = cellSetPut(s, u shr PageShift) var t = cellSetPut(s, u shr PageShift)
u = (u %% PageSize) /% MemAlign u = (u %% PageSize) /% MemAlign
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask)) t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
proc excl(s: var TCellSet, cell: PCell) = proc excl(s: var CellSet, cell: PCell) =
var u = cast[ByteAddress](cell) var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift) var t = cellSetGet(s, u shr PageShift)
if t != nil: if t != nil:
@ -169,7 +170,7 @@ proc excl(s: var TCellSet, cell: PCell) =
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
not (1 shl (u and IntMask))) not (1 shl (u and IntMask)))
proc containsOrIncl(s: var TCellSet, cell: PCell): bool = proc containsOrIncl(s: var CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell) var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift) var t = cellSetGet(s, u shr PageShift)
if t != nil: if t != nil:
@ -182,7 +183,7 @@ proc containsOrIncl(s: var TCellSet, cell: PCell): bool =
incl(s, cell) incl(s, cell)
result = false result = false
iterator elements(t: TCellSet): PCell {.inline.} = iterator elements(t: CellSet): PCell {.inline.} =
# while traversing it is forbidden to add pointers to the tree! # while traversing it is forbidden to add pointers to the tree!
var r = t.head var r = t.head
while r != nil: while r != nil:
@ -200,7 +201,7 @@ iterator elements(t: TCellSet): PCell {.inline.} =
inc(i) inc(i)
r = r.next r = r.next
iterator elementsExcept(t, s: TCellSet): PCell {.inline.} = iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
var r = t.head var r = t.head
while r != nil: while r != nil:
let ss = cellSetGet(s, r.key) let ss = cellSetGet(s, r.key)

View file

@ -12,12 +12,13 @@
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.} proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
type type
TLibHandle = pointer # private type LibHandle = pointer # private type
TProcAddr = pointer # library loading and loading of procs: ProcAddr = pointer # library loading and loading of procs:
{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].}
proc nimLoadLibrary(path: string): TLibHandle {.compilerproc.} proc nimLoadLibrary(path: string): LibHandle {.compilerproc.}
proc nimUnloadLibrary(lib: TLibHandle) {.compilerproc.} proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.}
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr {.compilerproc.} proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc.}
proc nimLoadLibraryError(path: string) {.compilerproc, noinline.} proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}

View file

@ -19,18 +19,20 @@ when not declared(NimString):
type type
pbytes = ptr array[0.. 0xffff, byte] pbytes = ptr array[0.. 0xffff, byte]
TRawChannel {.pure, final.} = object ## msg queue for a thread RawChannel {.pure, final.} = object ## msg queue for a thread
rd, wr, count, mask: int rd, wr, count, mask: int
data: pbytes data: pbytes
lock: TSysLock lock: SysLock
cond: TSysCond cond: SysCond
elemType: PNimType elemType: PNimType
ready: bool ready: bool
region: TMemRegion region: MemRegion
PRawChannel = ptr TRawChannel PRawChannel = ptr RawChannel
TLoadStoreMode = enum mStore, mLoad LoadStoreMode = enum mStore, mLoad
TChannel* {.gcsafe.}[TMsg] = TRawChannel ## a channel for thread communication Channel* {.gcsafe.}[TMsg] = RawChannel ## a channel for thread communication
{.deprecated: [TRawChannel: RawChannel, TLoadStoreMode: LoadStoreMode,
TChannel: Channel].}
const ChannelDeadMask = -2 const ChannelDeadMask = -2
proc initRawChannel(p: pointer) = proc initRawChannel(p: pointer) =
@ -49,9 +51,9 @@ proc deinitRawChannel(p: pointer) =
deinitSysCond(c.cond) deinitSysCond(c.cond)
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: TLoadStoreMode) {.benign.} mode: LoadStoreMode) {.benign.}
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
mode: TLoadStoreMode) {.benign.} = mode: LoadStoreMode) {.benign.} =
var var
d = cast[ByteAddress](dest) d = cast[ByteAddress](dest)
s = cast[ByteAddress](src) s = cast[ByteAddress](src)
@ -68,7 +70,7 @@ proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
of nkNone: sysAssert(false, "storeAux") of nkNone: sysAssert(false, "storeAux")
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: TLoadStoreMode) = mode: LoadStoreMode) =
var var
d = cast[ByteAddress](dest) d = cast[ByteAddress](dest)
s = cast[ByteAddress](src) s = cast[ByteAddress](src)
@ -202,7 +204,7 @@ template sendImpl(q: expr) {.immediate.} =
releaseSys(q.lock) releaseSys(q.lock)
signalSysCond(q.cond) signalSysCond(q.cond)
proc send*[TMsg](c: var TChannel[TMsg], msg: TMsg) = proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
## sends a message to a thread. `msg` is deeply copied. ## sends a message to a thread. `msg` is deeply copied.
var q = cast[PRawChannel](addr(c)) var q = cast[PRawChannel](addr(c))
sendImpl(q) sendImpl(q)
@ -218,7 +220,7 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
sysFatal(ValueError, "cannot receive message of wrong type") sysFatal(ValueError, "cannot receive message of wrong type")
rawRecv(q, res, typ) rawRecv(q, res, typ)
proc recv*[TMsg](c: var TChannel[TMsg]): TMsg = proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
## receives a message from the channel `c`. This blocks until ## receives a message from the channel `c`. This blocks until
## a message has arrived! You may use ``peek`` to avoid the blocking. ## a message has arrived! You may use ``peek`` to avoid the blocking.
var q = cast[PRawChannel](addr(c)) var q = cast[PRawChannel](addr(c))
@ -226,7 +228,7 @@ proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result))) llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
releaseSys(q.lock) releaseSys(q.lock)
proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool, proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
msg: TMsg] = msg: TMsg] =
## try to receives a message from the channel `c` if available. Otherwise ## try to receives a message from the channel `c` if available. Otherwise
## it returns ``(false, default(msg))``. ## it returns ``(false, default(msg))``.
@ -238,7 +240,7 @@ proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool,
result.dataAvailable = true result.dataAvailable = true
releaseSys(q.lock) releaseSys(q.lock)
proc peek*[TMsg](c: var TChannel[TMsg]): int = proc peek*[TMsg](c: var Channel[TMsg]): int =
## returns the current number of messages in the channel `c`. Returns -1 ## returns the current number of messages in the channel `c`. Returns -1
## if the channel has been closed. **Note**: This is dangerous to use ## if the channel has been closed. **Note**: This is dangerous to use
## as it encourages races. It's much better to use ``tryRecv`` instead. ## as it encourages races. It's much better to use ``tryRecv`` instead.
@ -249,15 +251,15 @@ proc peek*[TMsg](c: var TChannel[TMsg]): int =
else: else:
result = -1 result = -1
proc open*[TMsg](c: var TChannel[TMsg]) = proc open*[TMsg](c: var Channel[TMsg]) =
## opens a channel `c` for inter thread communication. ## opens a channel `c` for inter thread communication.
initRawChannel(addr(c)) initRawChannel(addr(c))
proc close*[TMsg](c: var TChannel[TMsg]) = proc close*[TMsg](c: var Channel[TMsg]) =
## closes a channel `c` and frees its associated resources. ## closes a channel `c` and frees its associated resources.
deinitRawChannel(addr(c)) deinitRawChannel(addr(c))
proc ready*[TMsg](c: var TChannel[TMsg]): bool = proc ready*[TMsg](c: var Channel[TMsg]): bool =
## returns true iff some thread is waiting on the channel `c` for ## returns true iff some thread is waiting on the channel `c` for
## new messages. ## new messages.
var q = cast[PRawChannel](addr(c)) var q = cast[PRawChannel](addr(c))

View file

@ -10,22 +10,23 @@
## This file implements basic features for any debugger. ## This file implements basic features for any debugger.
type type
TVarSlot* {.compilerproc, final.} = object ## a slot in a frame VarSlot* {.compilerproc, final.} = object ## a slot in a frame
address*: pointer ## the variable's address address*: pointer ## the variable's address
typ*: PNimType ## the variable's type typ*: PNimType ## the variable's type
name*: cstring ## the variable's name; for globals this is "module.name" name*: cstring ## the variable's name; for globals this is "module.name"
PExtendedFrame = ptr TExtendedFrame PExtendedFrame = ptr ExtendedFrame
TExtendedFrame = object # If the debugger is enabled the compiler ExtendedFrame = object # If the debugger is enabled the compiler
# provides an extended frame. Of course # provides an extended frame. Of course
# only slots that are # only slots that are
# needed are allocated and not 10_000, # needed are allocated and not 10_000,
# except for the global data description. # except for the global data description.
f: TFrame f: Frame
slots: array[0..10_000, TVarSlot] slots: array[0..10_000, VarSlot]
{.deprecated: [TVarSlot: VarSlot, TExtendedFrame: ExtendedFrame].}
var var
dbgGlobalData: TExtendedFrame # this reserves much space, but dbgGlobalData: ExtendedFrame # this reserves much space, but
# for now it is the most practical way # for now it is the most practical way
proc dbgRegisterGlobal(name: cstring, address: pointer, proc dbgRegisterGlobal(name: cstring, address: pointer,
@ -39,7 +40,7 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
dbgGlobalData.slots[i].address = address dbgGlobalData.slots[i].address = address
inc(dbgGlobalData.f.len) inc(dbgGlobalData.f.len)
proc getLocal*(frame: PFrame; slot: int): TVarSlot {.inline.} = proc getLocal*(frame: PFrame; slot: int): VarSlot {.inline.} =
## retrieves the meta data for the local variable at `slot`. CAUTION: An ## retrieves the meta data for the local variable at `slot`. CAUTION: An
## invalid `slot` value causes a corruption! ## invalid `slot` value causes a corruption!
result = cast[PExtendedFrame](frame).slots[slot] result = cast[PExtendedFrame](frame).slots[slot]
@ -48,7 +49,7 @@ proc getGlobalLen*(): int {.inline.} =
## gets the number of registered globals. ## gets the number of registered globals.
result = dbgGlobalData.f.len result = dbgGlobalData.f.len
proc getGlobal*(slot: int): TVarSlot {.inline.} = proc getGlobal*(slot: int): VarSlot {.inline.} =
## retrieves the meta data for the global variable at `slot`. CAUTION: An ## retrieves the meta data for the global variable at `slot`. CAUTION: An
## invalid `slot` value causes a corruption! ## invalid `slot` value causes a corruption!
result = dbgGlobalData.slots[slot] result = dbgGlobalData.slots[slot]
@ -56,13 +57,13 @@ proc getGlobal*(slot: int): TVarSlot {.inline.} =
# ------------------- breakpoint support ------------------------------------ # ------------------- breakpoint support ------------------------------------
type type
TBreakpoint* = object ## represents a break point Breakpoint* = object ## represents a break point
low*, high*: int ## range from low to high; if disabled low*, high*: int ## range from low to high; if disabled
## both low and high are set to their negative values ## both low and high are set to their negative values
filename*: cstring ## the filename of the breakpoint filename*: cstring ## the filename of the breakpoint
var var
dbgBP: array[0..127, TBreakpoint] # breakpoints dbgBP: array[0..127, Breakpoint] # breakpoints
dbgBPlen: int dbgBPlen: int
dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking
@ -131,16 +132,16 @@ proc canonFilename*(filename: cstring): cstring =
if fileMatches(result, filename): return result if fileMatches(result, filename): return result
result = nil result = nil
iterator listBreakpoints*(): ptr TBreakpoint = iterator listBreakpoints*(): ptr Breakpoint =
## lists all breakpoints. ## lists all breakpoints.
for i in 0..dbgBPlen-1: yield addr(dbgBP[i]) for i in 0..dbgBPlen-1: yield addr(dbgBP[i])
proc isActive*(b: ptr TBreakpoint): bool = b.low > 0 proc isActive*(b: ptr Breakpoint): bool = b.low > 0
proc flip*(b: ptr TBreakpoint) = proc flip*(b: ptr Breakpoint) =
## enables or disables 'b' depending on its current state. ## enables or disables 'b' depending on its current state.
b.low = -b.low; b.high = -b.high b.low = -b.low; b.high = -b.high
proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint = proc checkBreakpoints*(filename: cstring, line: int): ptr Breakpoint =
## in which breakpoint (if any) we are. ## in which breakpoint (if any) we are.
if (dbgBPbloom and line) != line: return nil if (dbgBPbloom and line) != line: return nil
for b in listBreakpoints(): for b in listBreakpoints():
@ -149,29 +150,30 @@ proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint =
# ------------------- watchpoint support ------------------------------------ # ------------------- watchpoint support ------------------------------------
type type
THash = int Hash = int
TWatchpoint {.pure, final.} = object Watchpoint {.pure, final.} = object
name: cstring name: cstring
address: pointer address: pointer
typ: PNimType typ: PNimType
oldValue: THash oldValue: Hash
{.deprecated: [THash: Hash, TWatchpoint: Watchpoint].}
var var
watchpoints: array [0..99, TWatchpoint] watchpoints: array [0..99, Watchpoint]
watchpointsLen: int watchpointsLen: int
proc `!&`(h: THash, val: int): THash {.inline.} = proc `!&`(h: Hash, val: int): Hash {.inline.} =
result = h +% val result = h +% val
result = result +% result shl 10 result = result +% result shl 10
result = result xor (result shr 6) result = result xor (result shr 6)
proc `!$`(h: THash): THash {.inline.} = proc `!$`(h: Hash): Hash {.inline.} =
result = h +% h shl 3 result = h +% h shl 3
result = result xor (result shr 11) result = result xor (result shr 11)
result = result +% result shl 15 result = result +% result shl 15
proc hash(data: pointer, size: int): THash = proc hash(data: pointer, size: int): Hash =
var h: THash = 0 var h: Hash = 0
var p = cast[cstring](data) var p = cast[cstring](data)
var i = 0 var i = 0
var s = size var s = size
@ -181,14 +183,14 @@ proc hash(data: pointer, size: int): THash =
dec(s) dec(s)
result = !$h result = !$h
proc hashGcHeader(data: pointer): THash = proc hashGcHeader(data: pointer): Hash =
const headerSize = sizeof(int)*2 const headerSize = sizeof(int)*2
result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize) result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize)
proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
h: THash): THash h: Hash): Hash
proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool, proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool,
h: THash): THash = h: Hash): Hash =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: of nkSlot:
@ -204,7 +206,7 @@ proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool,
of nkNone: sysAssert(false, "genericHashAux") of nkNone: sysAssert(false, "genericHashAux")
proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
h: THash): THash = h: Hash): Hash =
sysAssert(mt != nil, "genericHashAux 2") sysAssert(mt != nil, "genericHashAux 2")
case mt.kind case mt.kind
of tyString: of tyString:

View file

@ -15,7 +15,7 @@
{.push stack_trace: off.} {.push stack_trace: off.}
const const
NilLibHandle: TLibHandle = nil NilLibHandle: LibHandle = nil
proc rawWrite(f: File, s: string) = proc rawWrite(f: File, s: string) =
# we cannot throw an exception here! # we cannot throw an exception here!
@ -55,22 +55,22 @@ when defined(posix):
var var
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".} proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".}
proc dlopen(path: cstring, mode: int): TLibHandle {. proc dlopen(path: cstring, mode: int): LibHandle {.
importc, header: "<dlfcn.h>".} importc, header: "<dlfcn.h>".}
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {. proc dlsym(lib: LibHandle, name: cstring): ProcAddr {.
importc, header: "<dlfcn.h>".} importc, header: "<dlfcn.h>".}
proc dlerror(): cstring {.importc, header: "<dlfcn.h>".} proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
proc nimUnloadLibrary(lib: TLibHandle) = proc nimUnloadLibrary(lib: LibHandle) =
dlclose(lib) dlclose(lib)
proc nimLoadLibrary(path: string): TLibHandle = proc nimLoadLibrary(path: string): LibHandle =
result = dlopen(path, RTLD_NOW) result = dlopen(path, RTLD_NOW)
#c_fprintf(c_stdout, "%s\n", dlerror()) #c_fprintf(c_stdout, "%s\n", dlerror())
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr = proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = dlsym(lib, name) result = dlsym(lib, name)
if result == nil: procAddrError(name) if result == nil: procAddrError(name)
@ -84,12 +84,12 @@ elif defined(windows) or defined(dos):
type type
THINSTANCE {.importc: "HINSTANCE".} = object THINSTANCE {.importc: "HINSTANCE".} = object
x: pointer x: pointer
proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {. proc getProcAddress(lib: THINSTANCE, name: cstring): ProcAddr {.
importcpp: "(void*)GetProcAddress(@)", header: "<windows.h>", stdcall.} importcpp: "(void*)GetProcAddress(@)", header: "<windows.h>", stdcall.}
else: else:
type type
THINSTANCE {.importc: "HINSTANCE".} = pointer THINSTANCE {.importc: "HINSTANCE".} = pointer
proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {. proc getProcAddress(lib: THINSTANCE, name: cstring): ProcAddr {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.} importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc freeLibrary(lib: THINSTANCE) {. proc freeLibrary(lib: THINSTANCE) {.
@ -97,13 +97,13 @@ elif defined(windows) or defined(dos):
proc winLoadLibrary(path: cstring): THINSTANCE {. proc winLoadLibrary(path: cstring): THINSTANCE {.
importc: "LoadLibraryA", header: "<windows.h>", stdcall.} importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
proc nimUnloadLibrary(lib: TLibHandle) = proc nimUnloadLibrary(lib: LibHandle) =
freeLibrary(cast[THINSTANCE](lib)) freeLibrary(cast[THINSTANCE](lib))
proc nimLoadLibrary(path: string): TLibHandle = proc nimLoadLibrary(path: string): LibHandle =
result = cast[TLibHandle](winLoadLibrary(path)) result = cast[LibHandle](winLoadLibrary(path))
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr = proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = getProcAddress(cast[THINSTANCE](lib), name) result = getProcAddress(cast[THINSTANCE](lib), name)
if result == nil: procAddrError(name) if result == nil: procAddrError(name)
@ -115,13 +115,13 @@ elif defined(mac):
# #
{.error: "no implementation for dyncalls yet".} {.error: "no implementation for dyncalls yet".}
proc nimUnloadLibrary(lib: TLibHandle) = proc nimUnloadLibrary(lib: LibHandle) =
NSUnLinkModule(NSModule(lib), NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES) NSUnLinkModule(NSModule(lib), NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES)
var var
dyld_present {.importc: "_dyld_present", header: "<dyld.h>".}: int dyld_present {.importc: "_dyld_present", header: "<dyld.h>".}: int
proc nimLoadLibrary(path: string): TLibHandle = proc nimLoadLibrary(path: string): LibHandle =
var var
img: NSObjectFileImage img: NSObjectFileImage
ret: NSObjectFileImageReturnCode ret: NSObjectFileImageReturnCode
@ -134,13 +134,13 @@ elif defined(mac):
modul = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE or modul = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE or
NSLINKMODULE_OPTION_RETURN_ON_ERROR) NSLINKMODULE_OPTION_RETURN_ON_ERROR)
NSDestroyObjectFileImage(img) NSDestroyObjectFileImage(img)
result = TLibHandle(modul) result = LibHandle(modul)
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr = proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
var var
nss: NSSymbol nss: NSSymbol
nss = NSLookupSymbolInModule(NSModule(lib), name) nss = NSLookupSymbolInModule(NSModule(lib), name)
result = TProcAddr(NSAddressOfSymbol(nss)) result = ProcAddr(NSAddressOfSymbol(nss))
if result == nil: ProcAddrError(name) if result == nil: ProcAddrError(name)
else: else:

View file

@ -20,63 +20,65 @@ const
EndbEnd = "***\n" EndbEnd = "***\n"
type type
TStaticStr = object StaticStr = object
len: int len: int
data: array[0..100, char] data: array[0..100, char]
TBreakpointFilename = object BreakpointFilename = object
b: ptr TBreakpoint b: ptr Breakpoint
filename: TStaticStr filename: StaticStr
TDbgState = enum DbgState = enum
dbOff, # debugger is turned off dbOff, # debugger is turned off
dbStepInto, # debugger is in tracing mode dbStepInto, # debugger is in tracing mode
dbStepOver, dbStepOver,
dbSkipCurrent, dbSkipCurrent,
dbQuiting, # debugger wants to quit dbQuiting, # debugger wants to quit
dbBreakpoints # debugger is only interested in breakpoints dbBreakpoints # debugger is only interested in breakpoints
{.deprecated: [TStaticStr: StaticStr, TBreakpointFilename: BreakpointFilename,
TDbgState: DbgState].}
var var
dbgUser: TStaticStr # buffer for user input; first command is ``step_into`` dbgUser: StaticStr # buffer for user input; first command is ``step_into``
# needs to be global cause we store the last command # needs to be global cause we store the last command
# in it # in it
dbgState: TDbgState # state of debugger dbgState: DbgState # state of debugger
dbgSkipToFrame: PFrame # frame to be skipped to dbgSkipToFrame: PFrame # frame to be skipped to
maxDisplayRecDepth: int = 5 # do not display too much data! maxDisplayRecDepth: int = 5 # do not display too much data!
brkPoints: array[0..127, TBreakpointFilename] brkPoints: array[0..127, BreakpointFilename]
proc setLen(s: var TStaticStr, newLen=0) = proc setLen(s: var StaticStr, newLen=0) =
s.len = newLen s.len = newLen
s.data[newLen] = '\0' s.data[newLen] = '\0'
proc add(s: var TStaticStr, c: char) = proc add(s: var StaticStr, c: char) =
if s.len < high(s.data)-1: if s.len < high(s.data)-1:
s.data[s.len] = c s.data[s.len] = c
s.data[s.len+1] = '\0' s.data[s.len+1] = '\0'
inc s.len inc s.len
proc add(s: var TStaticStr, c: cstring) = proc add(s: var StaticStr, c: cstring) =
var i = 0 var i = 0
while c[i] != '\0': while c[i] != '\0':
add s, c[i] add s, c[i]
inc i inc i
proc assign(s: var TStaticStr, c: cstring) = proc assign(s: var StaticStr, c: cstring) =
setLen(s) setLen(s)
add s, c add s, c
proc `==`(a, b: TStaticStr): bool = proc `==`(a, b: StaticStr): bool =
if a.len == b.len: if a.len == b.len:
for i in 0 .. a.len-1: for i in 0 .. a.len-1:
if a.data[i] != b.data[i]: return false if a.data[i] != b.data[i]: return false
return true return true
proc `==`(a: TStaticStr, b: cstring): bool = proc `==`(a: StaticStr, b: cstring): bool =
result = c_strcmp(a.data, b) == 0 result = c_strcmp(a.data, b) == 0
proc write(f: TFile, s: TStaticStr) = proc write(f: File, s: StaticStr) =
write(f, cstring(s.data)) write(f, cstring(s.data))
proc listBreakPoints() = proc listBreakPoints() =
@ -95,14 +97,14 @@ proc listBreakPoints() =
write(stdout, "\n") write(stdout, "\n")
write(stdout, EndbEnd) write(stdout, EndbEnd)
proc openAppend(filename: cstring): TFile = proc openAppend(filename: cstring): File =
var p: pointer = fopen(filename, "ab") var p: pointer = fopen(filename, "ab")
if p != nil: if p != nil:
result = cast[TFile](p) result = cast[File](p)
write(result, "----------------------------------------\n") write(result, "----------------------------------------\n")
proc dbgRepr(p: pointer, typ: PNimType): string = proc dbgRepr(p: pointer, typ: PNimType): string =
var cl: TReprClosure var cl: ReprClosure
initReprClosure(cl) initReprClosure(cl)
cl.recDepth = maxDisplayRecDepth cl.recDepth = maxDisplayRecDepth
# locks for the GC turned out to be a bad idea... # locks for the GC turned out to be a bad idea...
@ -112,12 +114,12 @@ proc dbgRepr(p: pointer, typ: PNimType): string =
# dec(recGcLock) # dec(recGcLock)
deinitReprClosure(cl) deinitReprClosure(cl)
proc writeVariable(stream: TFile, slot: TVarSlot) = proc writeVariable(stream: File, slot: VarSlot) =
write(stream, slot.name) write(stream, slot.name)
write(stream, " = ") write(stream, " = ")
writeln(stream, dbgRepr(slot.address, slot.typ)) writeln(stream, dbgRepr(slot.address, slot.typ))
proc listFrame(stream: TFile, f: PFrame) = proc listFrame(stream: File, f: PFrame) =
write(stream, EndbBeg) write(stream, EndbBeg)
write(stream, "| Frame (") write(stream, "| Frame (")
write(stream, f.len) write(stream, f.len)
@ -126,7 +128,7 @@ proc listFrame(stream: TFile, f: PFrame) =
writeln(stream, getLocal(f, i).name) writeln(stream, getLocal(f, i).name)
write(stream, EndbEnd) write(stream, EndbEnd)
proc listLocals(stream: TFile, f: PFrame) = proc listLocals(stream: File, f: PFrame) =
write(stream, EndbBeg) write(stream, EndbBeg)
write(stream, "| Frame (") write(stream, "| Frame (")
write(stream, f.len) write(stream, f.len)
@ -135,7 +137,7 @@ proc listLocals(stream: TFile, f: PFrame) =
writeVariable(stream, getLocal(f, i)) writeVariable(stream, getLocal(f, i))
write(stream, EndbEnd) write(stream, EndbEnd)
proc listGlobals(stream: TFile) = proc listGlobals(stream: File) =
write(stream, EndbBeg) write(stream, EndbBeg)
write(stream, "| Globals:\n") write(stream, "| Globals:\n")
for i in 0 .. getGlobalLen()-1: for i in 0 .. getGlobalLen()-1:
@ -172,7 +174,7 @@ proc dbgShowExecutionPoint() =
write(stdout, framePtr.procname) write(stdout, framePtr.procname)
write(stdout, " ***\n") write(stdout, " ***\n")
proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int = proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int =
result = start result = start
# skip whitespace: # skip whitespace:
while src[result] in {'\t', ' '}: inc(result) while src[result] in {'\t', ' '}: inc(result)
@ -184,11 +186,11 @@ proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
else: break else: break
inc(result) inc(result)
proc scanWord(src: cstring, a: var TStaticStr, start: int): int = proc scanWord(src: cstring, a: var StaticStr, start: int): int =
setlen(a) setlen(a)
result = scanAndAppendWord(src, a, start) result = scanAndAppendWord(src, a, start)
proc scanFilename(src: cstring, a: var TStaticStr, start: int): int = proc scanFilename(src: cstring, a: var StaticStr, start: int): int =
result = start result = start
setLen a setLen a
while src[result] in {'\t', ' '}: inc(result) while src[result] in {'\t', ' '}: inc(result)
@ -250,8 +252,8 @@ proc hasExt(s: cstring): bool =
if s[i] == '.': return true if s[i] == '.': return true
inc i inc i
proc parseBreakpoint(s: cstring, start: int): TBreakpoint = proc parseBreakpoint(s: cstring, start: int): Breakpoint =
var dbgTemp: TStaticStr var dbgTemp: StaticStr
var i = scanNumber(s, result.low, start) var i = scanNumber(s, result.low, start)
if result.low == 0: result.low = framePtr.line if result.low == 0: result.low = framePtr.line
i = scanNumber(s, result.high, i) i = scanNumber(s, result.high, i)
@ -279,11 +281,11 @@ proc breakpointToggle(s: cstring, start: int) =
if not b.isNil: b.flip if not b.isNil: b.flip
else: debugOut("[Warning] unknown breakpoint ") else: debugOut("[Warning] unknown breakpoint ")
proc dbgEvaluate(stream: TFile, s: cstring, start: int, f: PFrame) = proc dbgEvaluate(stream: File, s: cstring, start: int, f: PFrame) =
var dbgTemp: TStaticStr var dbgTemp: StaticStr
var i = scanWord(s, dbgTemp, start) var i = scanWord(s, dbgTemp, start)
while s[i] in {' ', '\t'}: inc(i) while s[i] in {' ', '\t'}: inc(i)
var v: TVarSlot var v: VarSlot
if s[i] == '.': if s[i] == '.':
inc(i) inc(i)
add(dbgTemp, '.') add(dbgTemp, '.')
@ -299,7 +301,7 @@ proc dbgEvaluate(stream: TFile, s: cstring, start: int, f: PFrame) =
writeVariable(stream, v) writeVariable(stream, v)
proc dbgOut(s: cstring, start: int, currFrame: PFrame) = proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
var dbgTemp: TStaticStr var dbgTemp: StaticStr
var i = scanFilename(s, dbgTemp, start) var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0: if dbgTemp.len == 0:
invalidCommand() invalidCommand()
@ -312,7 +314,7 @@ proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
close(stream) close(stream)
proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) = proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
var dbgTemp: TStaticStr var dbgTemp: StaticStr
var i = scanFilename(s, dbgTemp, start) var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0: if dbgTemp.len == 0:
# just write it to stdout: # just write it to stdout:
@ -325,7 +327,7 @@ proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
listFrame(stream, currFrame) listFrame(stream, currFrame)
close(stream) close(stream)
proc readLine(f: TFile, line: var TStaticStr): bool = proc readLine(f: File, line: var StaticStr): bool =
while true: while true:
var c = fgetc(f) var c = fgetc(f)
if c < 0'i32: if c < 0'i32:
@ -358,7 +360,7 @@ proc commandPrompt() =
again = true again = true
dbgFramePtr = framePtr # for going down and up the stack dbgFramePtr = framePtr # for going down and up the stack
dbgDown = 0 # how often we did go down dbgDown = 0 # how often we did go down
dbgTemp: TStaticStr dbgTemp: StaticStr
while again: while again:
write(stdout, "*** endb| >>") write(stdout, "*** endb| >>")

View file

@ -365,5 +365,6 @@ when not defined(noSignalHandler):
proc setControlCHook(hook: proc () {.noconv.} not nil) = proc setControlCHook(hook: proc () {.noconv.} not nil) =
# ugly cast, but should work on all architectures: # ugly cast, but should work on all architectures:
type TSignalHandler = proc (sig: cint) {.noconv, benign.} type SignalHandler = proc (sig: cint) {.noconv, benign.}
c_signal(SIGINT, cast[TSignalHandler](hook)) {.deprecated: [TSignalHandler: SignalHandler].}
c_signal(SIGINT, cast[SignalHandler](hook))

View file

@ -45,17 +45,17 @@ const
rcShift = 3 # shift by rcShift to get the reference counter rcShift = 3 # shift by rcShift to get the reference counter
colorMask = 0b011 colorMask = 0b011
type type
TWalkOp = enum WalkOp = enum
waMarkGlobal, # part of the backup/debug mark&sweep waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
waCollectWhite #, waDebug waCollectWhite #, waDebug
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGcStat {.final, pure.} = object GcStat {.final, pure.} = object
stackScans: int # number of performed stack scans (for statistics) stackScans: int # number of performed stack scans (for statistics)
cycleCollections: int # number of performed full collections cycleCollections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
@ -64,35 +64,36 @@ type
cycleTableSize: int # max entries in cycle table cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and GcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
cycleThreshold: int cycleThreshold: int
when useCellIds: when useCellIds:
idGenerator: int idGenerator: int
zct: TCellSeq # the zero count table zct: CellSeq # the zero count table
decStack: TCellSeq # cells in the stack that are to decref again decStack: CellSeq # cells in the stack that are to decref again
cycleRoots: TCellSet cycleRoots: CellSet
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: CellSeq # temporary stack for recursion elimination
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
when withRealTime: when withRealTime:
maxPause: TNanos # max allowed pause in nanoseconds; active if > 0 maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: TMemRegion # garbage collected region region: MemRegion # garbage collected region
stat: TGcStat stat: GcStat
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
marked: TCellSet marked: CellSet
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcHeap: GcHeap,
TGcStat: GcStat].}
var var
gch {.rtlThreadVar.}: TGcHeap gch {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -104,18 +105,18 @@ template gcAssert(cond: bool, msg: string) =
writeStackTrace() writeStackTrace()
quit 1 quit 1
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} = proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
if (c.refcount and ZctFlag) == 0: if (c.refcount and ZctFlag) == 0:
c.refcount = c.refcount or ZctFlag c.refcount = c.refcount or ZctFlag
add(s, c) add(s, c)
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
proc usrToCell(usr: pointer): PCell {.inline.} = proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
proc canBeCycleRoot(c: PCell): bool {.inline.} = proc canBeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
@ -152,11 +153,11 @@ template gcTrace(cell, state: expr): stmt {.immediate.} =
when traceGC: traceCell(cell, state) when traceGC: traceCell(cell, state)
# forward declarations: # forward declarations:
proc collectCT(gch: var TGcHeap) {.benign.} proc collectCT(gch: var GcHeap) {.benign.}
proc isOnStack*(p: pointer): bool {.noinline, benign.} proc isOnStack*(p: pointer): bool {.noinline, benign.}
proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.} proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
proc doOperation(p: pointer, op: TWalkOp) {.benign.} proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
@ -178,7 +179,7 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock. # prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil! # XXX: we should set the cell's children to nil!
inc(gch.recGcLock) inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
@ -276,7 +277,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
proc initGC() = proc initGC() =
when not defined(useNimRtl): when not defined(useNimRtl):
when traceGC: when traceGC:
for i in low(TCellState)..high(TCellState): init(states[i]) for i in low(CellState)..high(CellState): init(states[i])
gch.cycleThreshold = InitialCycleThreshold gch.cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.cycleCollections = 0
@ -308,12 +309,13 @@ proc setupForeignThreadGc*() =
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
type type
TGlobalMarkerProc = proc () {.nimcall, benign.} GlobalMarkerProc = proc () {.nimcall, benign.}
{.deprecated: [TGlobalMarkerProc: GlobalMarkerProc].}
var var
globalMarkersLen: int globalMarkersLen: int
globalMarkers: array[0.. 7_000, TGlobalMarkerProc] globalMarkers: array[0.. 7_000, GlobalMarkerProc]
proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} = proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
if globalMarkersLen <= high(globalMarkers): if globalMarkersLen <= high(globalMarkers):
globalMarkers[globalMarkersLen] = markerProc globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen inc globalMarkersLen
@ -321,11 +323,11 @@ when useMarkForDebug or useBackupGc:
echo "[GC] cannot register global variable; too many global variables" echo "[GC] cannot register global variable; too many global variables"
quit 1 quit 1
proc cellsetReset(s: var TCellSet) = proc cellsetReset(s: var CellSet) =
deinit(s) deinit(s)
init(s) init(s)
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@ -345,7 +347,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} =
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux") of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
@ -359,7 +361,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: discard else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
gcAssert(cell.typ != nil, "forAllChildren: 3") gcAssert(cell.typ != nil, "forAllChildren: 3")
@ -380,7 +382,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
else: discard else: discard
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused. # we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck # In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down. # out of this small linear search so that ``newObj`` is not slowed down.
@ -431,13 +433,13 @@ proc gcInvariant*() =
markForDebug(gch) markForDebug(gch)
{.pop.} {.pop.}
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
res.typ = typ res.typ = typ
@ -486,7 +488,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
@ -515,7 +517,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
@ -523,13 +525,13 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
sysAssert(allocInv(gch.region), "growObj begin") sysAssert(allocInv(gch.region), "growObj begin")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
# This can be wrong for intermediate temps that are nevertheless on the # This can be wrong for intermediate temps that are nevertheless on the
@ -564,7 +566,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
decRef(ol) decRef(ol)
else: else:
sysAssert(ol.typ != nil, "growObj: 5") sysAssert(ol.typ != nil, "growObj: 5")
zeroMem(ol, sizeof(TCell)) zeroMem(ol, sizeof(Cell))
release(gch) release(gch)
when useCellIds: when useCellIds:
inc gch.idGenerator inc gch.idGenerator
@ -580,7 +582,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ---------------- cycle collector ------------------------------------------- # ---------------- cycle collector -------------------------------------------
proc freeCyclicCell(gch: var TGcHeap, c: PCell) = proc freeCyclicCell(gch: var GcHeap, c: PCell) =
prepareDealloc(c) prepareDealloc(c)
gcTrace(c, csCycFreed) gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c) when logGC: writeCell("cycle collector dealloc cell", c)
@ -589,7 +591,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
gcAssert(c.typ != nil, "freeCyclicCell") gcAssert(c.typ != nil, "freeCyclicCell")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
proc markGray(s: PCell) = proc markGray(s: PCell) =
if s.color != rcGray: if s.color != rcGray:
@ -620,7 +622,7 @@ proc collectWhite(s: PCell) =
forAllChildren(s, waCollectWhite) forAllChildren(s, waCollectWhite)
freeCyclicCell(gch, s) freeCyclicCell(gch, s)
proc markRoots(gch: var TGcHeap) = proc markRoots(gch: var GcHeap) =
var tabSize = 0 var tabSize = 0
for s in elements(gch.cycleRoots): for s in elements(gch.cycleRoots):
#writeCell("markRoot", s) #writeCell("markRoot", s)
@ -635,7 +637,7 @@ proc markRoots(gch: var TGcHeap) =
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize) gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
when useBackupGc: when useBackupGc:
proc sweep(gch: var TGcHeap) = proc sweep(gch: var GcHeap) =
for x in allObjects(gch.region): for x in allObjects(gch.region):
if isCell(x): if isCell(x):
# cast to PCell is correct here: # cast to PCell is correct here:
@ -643,7 +645,7 @@ when useBackupGc:
if c notin gch.marked: freeCyclicCell(gch, c) if c notin gch.marked: freeCyclicCell(gch, c)
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markS(gch: var TGcHeap, c: PCell) = proc markS(gch: var GcHeap, c: PCell) =
incl(gch.marked, c) incl(gch.marked, c)
gcAssert gch.tempStack.len == 0, "stack not empty!" gcAssert gch.tempStack.len == 0, "stack not empty!"
forAllChildren(c, waMarkPrecise) forAllChildren(c, waMarkPrecise)
@ -653,10 +655,10 @@ when useMarkForDebug or useBackupGc:
if not containsOrIncl(gch.marked, d): if not containsOrIncl(gch.marked, d):
forAllChildren(d, waMarkPrecise) forAllChildren(d, waMarkPrecise)
proc markGlobals(gch: var TGcHeap) = proc markGlobals(gch: var GcHeap) =
for i in 0 .. < globalMarkersLen: globalMarkers[i]() for i in 0 .. < globalMarkersLen: globalMarkers[i]()
proc stackMarkS(gch: var TGcHeap, p: pointer) {.inline.} = proc stackMarkS(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
var cell = usrToCell(p) var cell = usrToCell(p)
var c = cast[TAddress](cell) var c = cast[TAddress](cell)
@ -688,7 +690,7 @@ when logGC:
forAllChildren(s, waDebug) forAllChildren(s, waDebug)
c_fprintf(c_stdout, "}\n") c_fprintf(c_stdout, "}\n")
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
gcAssert(c != nil, "doOperation: 1") gcAssert(c != nil, "doOperation: 1")
@ -733,19 +735,19 @@ proc doOperation(p: pointer, op: TWalkOp) =
#of waDebug: debugGraph(c) #of waDebug: debugGraph(c)
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, WalkOp(op))
proc collectZCT(gch: var TGcHeap): bool {.benign.} proc collectZCT(gch: var GcHeap): bool {.benign.}
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl, proc markStackAndRegistersForSweep(gch: var GcHeap) {.noinline, cdecl,
benign.} benign.}
proc collectRoots(gch: var TGcHeap) = proc collectRoots(gch: var GcHeap) =
for s in elements(gch.cycleRoots): for s in elements(gch.cycleRoots):
collectWhite(s) collectWhite(s)
proc collectCycles(gch: var TGcHeap) = proc collectCycles(gch: var GcHeap) =
# ensure the ZCT 'color' is not used: # ensure the ZCT 'color' is not used:
while gch.zct.len > 0: discard collectZCT(gch) while gch.zct.len > 0: discard collectZCT(gch)
when useBackupGc: when useBackupGc:
@ -778,7 +780,7 @@ proc collectCycles(gch: var TGcHeap) =
if cycleRootsLen != 0: if cycleRootsLen != 0:
cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen) cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen)
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
sysAssert(allocInv(gch.region), "gcMark begin") sysAssert(allocInv(gch.region), "gcMark begin")
var cell = usrToCell(p) var cell = usrToCell(p)
@ -798,7 +800,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell) add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) = proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".} {.error: "not fully implemented".}
var it = threadList var it = threadList
@ -887,7 +889,7 @@ elif stackIncreases:
var var
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
# a little hack to get the size of a TJmpBuf in the generated C code # a little hack to get the size of a JmpBuf in the generated C code
# in a platform independent way # in a platform independent way
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
@ -947,18 +949,18 @@ else:
gcMark(gch, cast[PPointer](sp)[]) gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer) sp = sp +% sizeof(pointer)
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark) forEachStackSlot(gch, gcMark)
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markStackAndRegistersForSweep(gch: var TGcHeap) = proc markStackAndRegistersForSweep(gch: var GcHeap) =
forEachStackSlot(gch, stackMarkS) forEachStackSlot(gch, stackMarkS)
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# end of non-portable code # end of non-portable code
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc collectZCT(gch: var TGcHeap): bool = proc collectZCT(gch: var GcHeap): bool =
# Note: Freeing may add child objects to the ZCT! So essentially we do # Note: Freeing may add child objects to the ZCT! So essentially we do
# deep freeing, which is bad for incremental operation. In order to # deep freeing, which is bad for incremental operation. In order to
# avoid a deep stack, we move objects to keep the ZCT small. # avoid a deep stack, we move objects to keep the ZCT small.
@ -968,7 +970,7 @@ proc collectZCT(gch: var TGcHeap): bool =
when withRealTime: when withRealTime:
var steps = workPackage var steps = workPackage
var t0: TTicks var t0: Ticks
if gch.maxPause > 0: t0 = getticks() if gch.maxPause > 0: t0 = getticks()
while L[] > 0: while L[] > 0:
var c = gch.zct.d[0] var c = gch.zct.d[0]
@ -1001,7 +1003,7 @@ proc collectZCT(gch: var TGcHeap): bool =
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
sysAssert(c.typ != nil, "collectZCT 2") sysAssert(c.typ != nil, "collectZCT 2")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
when withRealTime: when withRealTime:
if steps == 0: if steps == 0:
steps = workPackage steps = workPackage
@ -1014,7 +1016,7 @@ proc collectZCT(gch: var TGcHeap): bool =
return false return false
result = true result = true
proc unmarkStackAndRegisters(gch: var TGcHeap) = proc unmarkStackAndRegisters(gch: var GcHeap) =
var d = gch.decStack.d var d = gch.decStack.d
for i in 0..gch.decStack.len-1: for i in 0..gch.decStack.len-1:
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
@ -1026,7 +1028,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
#sysAssert c.typ != nil, "unmarkStackAndRegisters 2" #sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
gch.decStack.len = 0 gch.decStack.len = 0
proc collectCTBody(gch: var TGcHeap) = proc collectCTBody(gch: var GcHeap) =
when withRealTime: when withRealTime:
let t0 = getticks() let t0 = getticks()
sysAssert(allocInv(gch.region), "collectCT: begin") sysAssert(allocInv(gch.region), "collectCT: begin")
@ -1058,11 +1060,11 @@ proc collectCTBody(gch: var TGcHeap) =
c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration) c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markForDebug(gch: var TGcHeap) = proc markForDebug(gch: var GcHeap) =
markStackAndRegistersForSweep(gch) markStackAndRegistersForSweep(gch)
markGlobals(gch) markGlobals(gch)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var GcHeap) =
# stackMarkCosts prevents some pathological behaviour: Stack marking # stackMarkCosts prevents some pathological behaviour: Stack marking
# becomes more expensive with large stacks and large stacks mean that # becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack. # cells with RC=0 are more likely to be kept alive by the stack.
@ -1077,13 +1079,13 @@ proc collectCT(gch: var TGcHeap) =
collectCTBody(gch) collectCTBody(gch)
when withRealTime: when withRealTime:
proc toNano(x: int): TNanos {.inline.} = proc toNano(x: int): Nanos {.inline.} =
result = x * 1000 result = x * 1000
proc GC_setMaxPause*(MaxPauseInUs: int) = proc GC_setMaxPause*(MaxPauseInUs: int) =
gch.maxPause = MaxPauseInUs.toNano gch.maxPause = MaxPauseInUs.toNano
proc GC_step(gch: var TGcHeap, us: int, strongAdvice: bool) = proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
acquire(gch) acquire(gch)
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and

View file

@ -54,7 +54,7 @@ const
# was replaced by a resize operation. # was replaced by a resize operation.
# see growObj for details # see growObj for details
rcColorMask = TRefCount(0b00111) rcColorMask = RefCount(0b00111)
rcZct = 0b01000 # already added to ZCT rcZct = 0b01000 # already added to ZCT
rcInCycleRoots = 0b10000 # already buffered as cycle candidate rcInCycleRoots = 0b10000 # already buffered as cycle candidate
@ -97,14 +97,14 @@ const
CollectCyclesStats = false CollectCyclesStats = false
type type
TWalkOp = enum WalkOp = enum
waPush waPush
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.} Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGcStat {.final, pure.} = object GcStat {.final, pure.} = object
stackScans: int # number of performed stack scans (for statistics) stackScans: int # number of performed stack scans (for statistics)
cycleCollections: int # number of performed full collections cycleCollections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
@ -113,16 +113,16 @@ type
cycleTableSize: int # max entries in cycle table cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and GcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
stackTop: pointer stackTop: pointer
cycleThreshold: int cycleThreshold: int
zct: TCellSeq # the zero count table zct: CellSeq # the zero count table
decStack: TCellSeq # cells in the stack that are to decref again decStack: CellSeq # cells in the stack that are to decref again
cycleRoots: TCellSeq cycleRoots: CellSeq
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: CellSeq # temporary stack for recursion elimination
freeStack: TCellSeq # objects ready to be freed freeStack: CellSeq # objects ready to be freed
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
# cycle roots table that uses a cheap linear scan # cycle roots table that uses a cheap linear scan
@ -132,21 +132,22 @@ type
# This index indicates the start of the range of # This index indicates the start of the range of
# such new objects within the table. # such new objects within the table.
when withRealTime: when withRealTime:
maxPause: TNanos # max allowed pause in nanoseconds; active if > 0 maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: TMemRegion # garbage collected region region: MemRegion # garbage collected region
stat: TGcStat stat: GcStat
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat,
TGcHeap: GcHeap].}
var var
gch* {.rtlThreadVar.}: TGcHeap gch* {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
AcquireSys(HeapLock) AcquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -169,7 +170,7 @@ template isDead(c: Pcell): expr =
c.isBitUp(rcReallyDead) # also covers rcRetiredBuffer c.isBitUp(rcReallyDead) # also covers rcRetiredBuffer
template clearBit(c: PCell, bit): expr = template clearBit(c: PCell, bit): expr =
c.refcount = c.refcount and (not TRefCount(bit)) c.refcount = c.refcount and (not RefCount(bit))
when debugGC: when debugGC:
var gcCollectionIdx = 0 var gcCollectionIdx = 0
@ -206,7 +207,7 @@ when debugGC:
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n", c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n",
msg, c, kind, c.refcount shr rcShift) msg, c, kind, c.refcount shr rcShift)
proc addZCT(zct: var TCellSeq, c: PCell) {.noinline.} = proc addZCT(zct: var CellSeq, c: PCell) {.noinline.} =
if c.isBitDown(rcZct): if c.isBitDown(rcZct):
c.setBit rcZct c.setBit rcZct
zct.add c zct.add c
@ -221,7 +222,7 @@ template setStackTop(gch) =
var stackTop {.volatile.}: pointer var stackTop {.volatile.}: pointer
gch.stackTop = addr(stackTop) gch.stackTop = addr(stackTop)
template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) = template addCycleRoot(cycleRoots: var CellSeq, c: PCell) =
if c.color != rcCycleCandidate: if c.color != rcCycleCandidate:
c.setColor rcCycleCandidate c.setColor rcCycleCandidate
@ -233,11 +234,11 @@ template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) =
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
proc usrToCell*(usr: pointer): PCell {.inline.} = proc usrToCell*(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
proc canbeCycleRoot(c: PCell): bool {.inline.} = proc canbeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
@ -254,11 +255,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# forward declarations: # forward declarations:
proc collectCT(gch: var TGcHeap) proc collectCT(gch: var GcHeap)
proc isOnStack*(p: pointer): bool {.noinline.} proc isOnStack*(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp) proc forAllChildren(cell: PCell, op: WalkOp)
proc doOperation(p: pointer, op: TWalkOp) proc doOperation(p: pointer, op: WalkOp)
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp)
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) = proc prepareDealloc(cell: PCell) =
@ -269,18 +270,19 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock. # prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil! # XXX: we should set the cell's children to nil!
inc(gch.recGcLock) inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
when traceGC: when traceGC:
# traceGC is a special switch to enable extensive debugging # traceGC is a special switch to enable extensive debugging
type type
TCellState = enum CellState = enum
csAllocated, csFreed csAllocated, csFreed
{.deprecated: [TCellState: CellState].}
var var
states: array[TCellState, TCellSet] states: array[CellState, CellSet]
proc traceCell(c: PCell, state: TCellState) = proc traceCell(c: PCell, state: CellState) =
case state case state
of csAllocated: of csAllocated:
if c in states[csAllocated]: if c in states[csAllocated]:
@ -300,7 +302,7 @@ when traceGC:
incl(states[state], c) incl(states[state], c)
proc computeCellWeight(c: PCell): int = proc computeCellWeight(c: PCell): int =
var x: TCellSet var x: CellSet
x.init x.init
let startLen = gch.tempStack.len let startLen = gch.tempStack.len
@ -363,30 +365,32 @@ proc rtlAddZCT(c: PCell) {.rtl, inl.} =
WithHeapLock: addZCT(gch.zct, c) WithHeapLock: addZCT(gch.zct, c)
type type
TCyclicMode = enum CyclicMode = enum
Cyclic, Cyclic,
Acyclic, Acyclic,
MaybeCyclic MaybeCyclic
TReleaseType = enum ReleaseType = enum
AddToZTC AddToZTC
FreeImmediately FreeImmediately
THeapType = enum HeapType = enum
LocalHeap LocalHeap
SharedHeap SharedHeap
{.deprecated: [TCyclicMode: CyclicMode, TReleaseType: ReleaseType,
THeapType: HeapType].}
template `++` (rc: TRefCount, heapType: THeapType): stmt = template `++` (rc: RefCount, heapType: HeapType): stmt =
when heapType == SharedHeap: when heapType == SharedHeap:
discard atomicInc(rc, rcIncrement) discard atomicInc(rc, rcIncrement)
else: else:
inc rc, rcIncrement inc rc, rcIncrement
template `--`(rc: TRefCount): expr = template `--`(rc: RefCount): expr =
dec rc, rcIncrement dec rc, rcIncrement
rc <% rcIncrement rc <% rcIncrement
template `--` (rc: TRefCount, heapType: THeapType): expr = template `--` (rc: RefCount, heapType: HeapType): expr =
(when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement else: --rc) (when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement else: --rc)
template doDecRef(cc: PCell, template doDecRef(cc: PCell,
@ -479,7 +483,7 @@ when hasThreadSupport and hasSharedHeap:
proc initGC() = proc initGC() =
when not defined(useNimRtl): when not defined(useNimRtl):
when traceGC: when traceGC:
for i in low(TCellState)..high(TCellState): init(states[i]) for i in low(CellState)..high(CellState): init(states[i])
gch.cycleThreshold = InitialCycleThreshold gch.cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.cycleCollections = 0
@ -494,7 +498,7 @@ proc initGC() =
init(gch.cycleRoots) init(gch.cycleRoots)
init(gch.decStack) init(gch.decStack)
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@ -514,7 +518,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux") of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
@ -528,7 +532,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: discard else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
sysAssert(cell != nil, "forAllChildren: 1") sysAssert(cell != nil, "forAllChildren: 1")
sysAssert(cell.typ != nil, "forAllChildren: 2") sysAssert(cell.typ != nil, "forAllChildren: 2")
sysAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3" sysAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
@ -549,7 +553,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
cell.typ.base, op) cell.typ.base, op)
else: discard else: discard
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused. # we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck # In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down. # out of this small linear search so that ``newObj`` is not slowed down.
@ -593,7 +597,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return return
add(gch.zct, res) add(gch.zct, res)
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
acquire(gch) acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")
@ -602,7 +606,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "rawNewObj after collect") sysAssert(allocInv(gch.region), "rawNewObj after collect")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc") sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@ -638,20 +642,20 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer
{.pop.} {.pop.}
proc freeCell(gch: var TGcHeap, c: PCell) = proc freeCell(gch: var GcHeap, c: PCell) =
# prepareDealloc(c) # prepareDealloc(c)
gcTrace(c, csFreed) gcTrace(c, csFreed)
when reallyDealloc: rawDealloc(gch.region, c) when reallyDealloc: rawDealloc(gch.region, c)
else: else:
sysAssert(c.typ != nil, "collectCycles") sysAssert(c.typ != nil, "collectCycles")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
template eraseAt(cells: var TCellSeq, at: int): stmt = template eraseAt(cells: var CellSeq, at: int): stmt =
cells.d[at] = cells.d[cells.len - 1] cells.d[at] = cells.d[cells.len - 1]
dec cells.len dec cells.len
template trimAt(roots: var TCellSeq, at: int): stmt = template trimAt(roots: var CellSeq, at: int): stmt =
# This will remove a cycle root candidate during trimming. # This will remove a cycle root candidate during trimming.
# a candidate is removed either because it received a refup and # a candidate is removed either because it received a refup and
# it's no longer a candidate or because it received further refdowns # it's no longer a candidate or because it received further refdowns
@ -696,7 +700,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
@ -704,7 +708,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
sysAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") sysAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
sysAssert(allocInv(gch.region), "growObj begin") sysAssert(allocInv(gch.region), "growObj begin")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = if ol.typ.kind != tyString: ol.typ.base.size var elemSize = if ol.typ.kind != tyString: ol.typ.base.size
else: 1 else: 1
@ -713,8 +717,8 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
# XXX: This should happen outside # XXX: This should happen outside
# call user-defined move code # call user-defined move code
# call user-defined default constructor # call user-defined default constructor
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
@ -778,26 +782,27 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ---------------- cycle collector ------------------------------------------- # ---------------- cycle collector -------------------------------------------
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
sysAssert(c != nil, "doOperation: 1") sysAssert(c != nil, "doOperation: 1")
gch.tempStack.add c gch.tempStack.add c
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, WalkOp(op))
type type
TRecursionType = enum RecursionType = enum
FromChildren, FromChildren,
FromRoot FromRoot
{.deprecated: [TRecursionType: RecursionType].}
proc collectZCT(gch: var TGcHeap): bool proc collectZCT(gch: var GcHeap): bool
template pseudoRecursion(typ: TRecursionType, body: stmt): stmt = template pseudoRecursion(typ: RecursionType, body: stmt): stmt =
discard discard
proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) = proc trimCycleRoots(gch: var GcHeap, startIdx = gch.cycleRootsTrimIdx) =
var i = startIdx var i = startIdx
while i < gch.cycleRoots.len: while i < gch.cycleRoots.len:
if gch.cycleRoots.d[i].color != rcCycleCandidate: if gch.cycleRoots.d[i].color != rcCycleCandidate:
@ -808,7 +813,7 @@ proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) =
gch.cycleRootsTrimIdx = gch.cycleRoots.len gch.cycleRootsTrimIdx = gch.cycleRoots.len
# we now use a much simpler and non-recursive algorithm for cycle removal # we now use a much simpler and non-recursive algorithm for cycle removal
proc collectCycles(gch: var TGcHeap) = proc collectCycles(gch: var GcHeap) =
if gch.cycleRoots.len == 0: return if gch.cycleRoots.len == 0: return
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, gch.cycleRoots.len) gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, gch.cycleRoots.len)
@ -990,7 +995,7 @@ var gcDebugging* = false
var seqdbg* : proc (s: PGenericSeq) {.cdecl.} var seqdbg* : proc (s: PGenericSeq) {.cdecl.}
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
sysAssert(allocInv(gch.region), "gcMark begin") sysAssert(allocInv(gch.region), "gcMark begin")
var cell = usrToCell(p) var cell = usrToCell(p)
@ -1025,7 +1030,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell) add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) = proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".} {.error: "not fully implemented".}
var it = threadList var it = threadList
@ -1074,7 +1079,7 @@ proc stackSize(): int {.noinline.} =
var var
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
# a little hack to get the size of a TJmpBuf in the generated C code # a little hack to get the size of a JmpBuf in the generated C code
# in a platform independent way # in a platform independent way
when defined(sparc): # For SPARC architecture. when defined(sparc): # For SPARC architecture.
@ -1086,7 +1091,7 @@ when defined(sparc): # For SPARC architecture.
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
when defined(sparcv9): when defined(sparcv9):
asm """"flushw \n" """ asm """"flushw \n" """
else: else:
@ -1117,7 +1122,7 @@ elif stackIncreases:
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
var registers: C_JmpBuf var registers: C_JmpBuf
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
var max = cast[ByteAddress](gch.stackBottom) var max = cast[ByteAddress](gch.stackBottom)
@ -1140,7 +1145,7 @@ else:
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
# We use a jmp_buf buffer that is in the C stack. # We use a jmp_buf buffer that is in the C stack.
# Used to traverse the stack and registers assuming # Used to traverse the stack and registers assuming
# that 'setjmp' will save registers in the C stack. # that 'setjmp' will save registers in the C stack.
@ -1181,7 +1186,7 @@ else:
# end of non-portable code # end of non-portable code
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc releaseCell(gch: var TGcHeap, cell: PCell) = proc releaseCell(gch: var GcHeap, cell: PCell) =
if cell.color != rcReallyDead: if cell.color != rcReallyDead:
prepareDealloc(cell) prepareDealloc(cell)
cell.setColor rcReallyDead cell.setColor rcReallyDead
@ -1210,13 +1215,13 @@ proc releaseCell(gch: var TGcHeap, cell: PCell) =
# recursion). # recursion).
# We can ignore it now as the ZCT cleaner will reach it soon. # We can ignore it now as the ZCT cleaner will reach it soon.
proc collectZCT(gch: var TGcHeap): bool = proc collectZCT(gch: var GcHeap): bool =
const workPackage = 100 const workPackage = 100
var L = addr(gch.zct.len) var L = addr(gch.zct.len)
when withRealtime: when withRealtime:
var steps = workPackage var steps = workPackage
var t0: TTicks var t0: Ticks
if gch.maxPause > 0: t0 = getticks() if gch.maxPause > 0: t0 = getticks()
while L[] > 0: while L[] > 0:
@ -1257,7 +1262,7 @@ proc collectZCT(gch: var TGcHeap): bool =
#deInit(gch.zct) #deInit(gch.zct)
#init(gch.zct) #init(gch.zct)
proc unmarkStackAndRegisters(gch: var TGcHeap) = proc unmarkStackAndRegisters(gch: var GcHeap) =
var d = gch.decStack.d var d = gch.decStack.d
for i in 0 .. <gch.decStack.len: for i in 0 .. <gch.decStack.len:
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
@ -1283,7 +1288,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
gch.decStack.len = 0 gch.decStack.len = 0
proc collectCTBody(gch: var TGcHeap) = proc collectCTBody(gch: var GcHeap) =
when withRealtime: when withRealtime:
let t0 = getticks() let t0 = getticks()
when debugGC: inc gcCollectionIdx when debugGC: inc gcCollectionIdx
@ -1315,20 +1320,20 @@ proc collectCTBody(gch: var TGcHeap) =
if gch.maxPause > 0 and duration > gch.maxPause: if gch.maxPause > 0 and duration > gch.maxPause:
c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration) c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var GcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0: gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)
when withRealtime: when withRealtime:
proc toNano(x: int): TNanos {.inline.} = proc toNano(x: int): Nanos {.inline.} =
result = x * 1000 result = x * 1000
proc GC_setMaxPause*(MaxPauseInUs: int) = proc GC_setMaxPause*(MaxPauseInUs: int) =
gch.maxPause = MaxPauseInUs.toNano gch.maxPause = MaxPauseInUs.toNano
proc GC_step(gch: var TGcHeap, us: int, strongAdvice: bool) = proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
acquire(gch) acquire(gch)
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and

View file

@ -26,49 +26,50 @@ when defined(memProfiler):
proc nimProfile(requestedSize: int) proc nimProfile(requestedSize: int)
type type
TWalkOp = enum WalkOp = enum
waMarkGlobal, # we need to mark conservatively for global marker procs waMarkGlobal, # we need to mark conservatively for global marker procs
# as these may refer to a global var and not to a thread # as these may refer to a global var and not to a thread
# local # local
waMarkPrecise # fast precise marking waMarkPrecise # fast precise marking
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGlobalMarkerProc = proc () {.nimcall, benign.} GlobalMarkerProc = proc () {.nimcall, benign.}
TGcStat = object GcStat = object
collections: int # number of performed full collections collections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size maxStackSize: int # max stack size
freedObjects: int # max entries in cycle table freedObjects: int # max entries in cycle table
TGcHeap = object # this contains the zero count and GcHeap = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
cycleThreshold: int cycleThreshold: int
when useCellIds: when useCellIds:
idGenerator: int idGenerator: int
when withBitvectors: when withBitvectors:
allocated, marked: TCellSet allocated, marked: CellSet
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: CellSeq # temporary stack for recursion elimination
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
region: TMemRegion # garbage collected region region: MemRegion # garbage collected region
stat: TGcStat stat: GcStat
additionalRoots: TCellSeq # dummy roots for GC_ref/unref additionalRoots: CellSeq # dummy roots for GC_ref/unref
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat,
TGlobalMarkerProc: GlobalMarkerProc, TGcHeap, GcHeap].}
var var
gch {.rtlThreadVar.}: TGcHeap gch {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -80,11 +81,11 @@ template gcAssert(cond: bool, msg: string) =
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
proc usrToCell(usr: pointer): PCell {.inline.} = proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
proc canbeCycleRoot(c: PCell): bool {.inline.} = proc canbeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
@ -101,9 +102,9 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
var var
globalMarkersLen: int globalMarkersLen: int
globalMarkers: array[0.. 7_000, TGlobalMarkerProc] globalMarkers: array[0.. 7_000, GlobalMarkerProc]
proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} = proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
if globalMarkersLen <= high(globalMarkers): if globalMarkersLen <= high(globalMarkers):
globalMarkers[globalMarkersLen] = markerProc globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen inc globalMarkersLen
@ -116,11 +117,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# forward declarations: # forward declarations:
proc collectCT(gch: var TGcHeap) {.benign.} proc collectCT(gch: var GcHeap) {.benign.}
proc isOnStack*(p: pointer): bool {.noinline, benign.} proc isOnStack*(p: pointer): bool {.noinline, benign.}
proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.} proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
proc doOperation(p: pointer, op: TWalkOp) {.benign.} proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) = proc prepareDealloc(cell: PCell) =
@ -131,7 +132,7 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock. # prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil! # XXX: we should set the cell's children to nil!
inc(gch.recGcLock) inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
proc nimGCref(p: pointer) {.compilerProc.} = proc nimGCref(p: pointer) {.compilerProc.} =
@ -182,7 +183,7 @@ proc setupForeignThreadGc*() =
setStackBottom(addr(stackTop)) setStackBottom(addr(stackTop))
initGC() initGC()
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@ -194,7 +195,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} =
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux") of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
@ -208,7 +209,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: discard else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(cell.typ != nil, "forAllChildren: 2") gcAssert(cell.typ != nil, "forAllChildren: 2")
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3" gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
@ -228,12 +229,12 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
else: discard else: discard
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
res.typ = typ res.typ = typ
@ -285,20 +286,20 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
sysAssert(ol.typ != nil, "growObj: 1") sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
when false: when false:
@ -306,7 +307,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
when withBitvectors: excl(gch.allocated, ol) when withBitvectors: excl(gch.allocated, ol)
when reallyDealloc: rawDealloc(gch.region, ol) when reallyDealloc: rawDealloc(gch.region, ol)
else: else:
zeroMem(ol, sizeof(TCell)) zeroMem(ol, sizeof(Cell))
when withBitvectors: incl(gch.allocated, res) when withBitvectors: incl(gch.allocated, res)
when useCellIds: when useCellIds:
inc gch.idGenerator inc gch.idGenerator
@ -322,7 +323,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ----------------- collector ----------------------------------------------- # ----------------- collector -----------------------------------------------
proc mark(gch: var TGcHeap, c: PCell) = proc mark(gch: var GcHeap, c: PCell) =
when withBitvectors: when withBitvectors:
incl(gch.marked, c) incl(gch.marked, c)
gcAssert gch.tempStack.len == 0, "stack not empty!" gcAssert gch.tempStack.len == 0, "stack not empty!"
@ -344,7 +345,7 @@ proc mark(gch: var TGcHeap, c: PCell) =
d.refCount = rcBlack d.refCount = rcBlack
forAllChildren(d, waMarkPrecise) forAllChildren(d, waMarkPrecise)
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
gcAssert(c != nil, "doOperation: 1") gcAssert(c != nil, "doOperation: 1")
@ -359,17 +360,17 @@ proc doOperation(p: pointer, op: TWalkOp) =
of waMarkPrecise: add(gch.tempStack, c) of waMarkPrecise: add(gch.tempStack, c)
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, WalkOp(op))
proc freeCyclicCell(gch: var TGcHeap, c: PCell) = proc freeCyclicCell(gch: var GcHeap, c: PCell) =
inc gch.stat.freedObjects inc gch.stat.freedObjects
prepareDealloc(c) prepareDealloc(c)
when reallyDealloc: rawDealloc(gch.region, c) when reallyDealloc: rawDealloc(gch.region, c)
else: else:
gcAssert(c.typ != nil, "freeCyclicCell") gcAssert(c.typ != nil, "freeCyclicCell")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
proc sweep(gch: var TGcHeap) = proc sweep(gch: var GcHeap) =
when withBitvectors: when withBitvectors:
for c in gch.allocated.elementsExcept(gch.marked): for c in gch.allocated.elementsExcept(gch.marked):
gch.allocated.excl(c) gch.allocated.excl(c)
@ -391,12 +392,12 @@ when false:
writeStackTrace() writeStackTrace()
quit 1 quit 1
proc markGlobals(gch: var TGcHeap) = proc markGlobals(gch: var GcHeap) =
for i in 0 .. < globalMarkersLen: globalMarkers[i]() for i in 0 .. < globalMarkersLen: globalMarkers[i]()
let d = gch.additionalRoots.d let d = gch.additionalRoots.d
for i in 0 .. < gch.additionalRoots.len: mark(gch, d[i]) for i in 0 .. < gch.additionalRoots.len: mark(gch, d[i])
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
var cell = usrToCell(p) var cell = usrToCell(p)
var c = cast[ByteAddress](cell) var c = cast[ByteAddress](cell)
@ -446,7 +447,7 @@ when defined(sparc): # For SPARC architecture.
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
when defined(sparcv9): when defined(sparcv9):
asm """"flushw \n" """ asm """"flushw \n" """
else: else:
@ -479,10 +480,10 @@ elif stackIncreases:
var var
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
# a little hack to get the size of a TJmpBuf in the generated C code # a little hack to get the size of a JmpBuf in the generated C code
# in a platform independent way # in a platform independent way
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
var registers: C_JmpBuf var registers: C_JmpBuf
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
var max = cast[ByteAddress](gch.stackBottom) var max = cast[ByteAddress](gch.stackBottom)
@ -505,7 +506,7 @@ else:
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
# We use a jmp_buf buffer that is in the C stack. # We use a jmp_buf buffer that is in the C stack.
# Used to traverse the stack and registers assuming # Used to traverse the stack and registers assuming
# that 'setjmp' will save registers in the C stack. # that 'setjmp' will save registers in the C stack.
@ -543,7 +544,7 @@ else:
# end of non-portable code # end of non-portable code
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc collectCTBody(gch: var TGcHeap) = proc collectCTBody(gch: var GcHeap) =
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
prepareForInteriorPointerChecking(gch.region) prepareForInteriorPointerChecking(gch.region)
markStackAndRegisters(gch) markStackAndRegisters(gch)
@ -558,7 +559,7 @@ proc collectCTBody(gch: var TGcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var GcHeap) =
if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0: if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)

View file

@ -15,13 +15,13 @@ else:
proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.} proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.}
type type
PSafePoint = ptr TSafePoint PSafePoint = ptr SafePoint
TSafePoint {.compilerproc, final.} = object SafePoint {.compilerproc, final.} = object
prev: PSafePoint # points to next safe point prev: PSafePoint # points to next safe point
exc: ref Exception exc: ref Exception
PCallFrame = ptr TCallFrame PCallFrame = ptr CallFrame
TCallFrame {.importc, nodecl, final.} = object CallFrame {.importc, nodecl, final.} = object
prev: PCallFrame prev: PCallFrame
procname: cstring procname: cstring
line: int # current line number line: int # current line number
@ -33,6 +33,7 @@ type
lineNumber {.importc.}: int lineNumber {.importc.}: int
message {.importc.}: cstring message {.importc.}: cstring
stack {.importc.}: cstring stack {.importc.}: cstring
{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
var var
framePtr {.importc, nodecl, volatile.}: PCallFrame framePtr {.importc, nodecl, volatile.}: PCallFrame
@ -60,12 +61,13 @@ proc getCurrentExceptionMsg*(): string =
proc auxWriteStackTrace(f: PCallFrame): string = proc auxWriteStackTrace(f: PCallFrame): string =
type type
TTempFrame = tuple[procname: cstring, line: int] TempFrame = tuple[procname: cstring, line: int]
{.deprecated: [TTempFrame: TempFrame].}
var var
it = f it = f
i = 0 i = 0
total = 0 total = 0
tempFrames: array [0..63, TTempFrame] tempFrames: array [0..63, TempFrame]
while it != nil and i <= high(tempFrames): while it != nil and i <= high(tempFrames):
tempFrames[i].procname = it.procname tempFrames[i].procname = it.procname
tempFrames[i].line = it.line tempFrames[i].line = it.line
@ -260,17 +262,17 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
""" """
type type
TDocument {.importc.} = object of RootObj Document {.importc.} = object of RootObj
write: proc (text: cstring) {.nimcall.} write: proc (text: cstring) {.nimcall.}
writeln: proc (text: cstring) {.nimcall.} writeln: proc (text: cstring) {.nimcall.}
createAttribute: proc (identifier: cstring): ref TNode {.nimcall.} createAttribute: proc (identifier: cstring): ref Node {.nimcall.}
createElement: proc (identifier: cstring): ref TNode {.nimcall.} createElement: proc (identifier: cstring): ref Node {.nimcall.}
createTextNode: proc (identifier: cstring): ref TNode {.nimcall.} createTextNode: proc (identifier: cstring): ref Node {.nimcall.}
getElementById: proc (id: cstring): ref TNode {.nimcall.} getElementById: proc (id: cstring): ref Node {.nimcall.}
getElementsByName: proc (name: cstring): seq[ref TNode] {.nimcall.} getElementsByName: proc (name: cstring): seq[ref Node] {.nimcall.}
getElementsByTagName: proc (name: cstring): seq[ref TNode] {.nimcall.} getElementsByTagName: proc (name: cstring): seq[ref Node] {.nimcall.}
TNodeType* = enum NodeType* = enum
ElementNode = 1, ElementNode = 1,
AttributeNode, AttributeNode,
TextNode, TextNode,
@ -283,35 +285,36 @@ type
DocumentTypeNode, DocumentTypeNode,
DocumentFragmentNode, DocumentFragmentNode,
NotationNode NotationNode
TNode* {.importc.} = object of RootObj Node* {.importc.} = object of RootObj
attributes*: seq[ref TNode] attributes*: seq[ref Node]
childNodes*: seq[ref TNode] childNodes*: seq[ref Node]
data*: cstring data*: cstring
firstChild*: ref TNode firstChild*: ref Node
lastChild*: ref TNode lastChild*: ref Node
nextSibling*: ref TNode nextSibling*: ref Node
nodeName*: cstring nodeName*: cstring
nodeType*: TNodeType nodeType*: NodeType
nodeValue*: cstring nodeValue*: cstring
parentNode*: ref TNode parentNode*: ref Node
previousSibling*: ref TNode previousSibling*: ref Node
appendChild*: proc (child: ref TNode) {.nimcall.} appendChild*: proc (child: ref Node) {.nimcall.}
appendData*: proc (data: cstring) {.nimcall.} appendData*: proc (data: cstring) {.nimcall.}
cloneNode*: proc (copyContent: bool) {.nimcall.} cloneNode*: proc (copyContent: bool) {.nimcall.}
deleteData*: proc (start, len: int) {.nimcall.} deleteData*: proc (start, len: int) {.nimcall.}
getAttribute*: proc (attr: cstring): cstring {.nimcall.} getAttribute*: proc (attr: cstring): cstring {.nimcall.}
getAttributeNode*: proc (attr: cstring): ref TNode {.nimcall.} getAttributeNode*: proc (attr: cstring): ref Node {.nimcall.}
getElementsByTagName*: proc (): seq[ref TNode] {.nimcall.} getElementsByTagName*: proc (): seq[ref Node] {.nimcall.}
hasChildNodes*: proc (): bool {.nimcall.} hasChildNodes*: proc (): bool {.nimcall.}
insertBefore*: proc (newNode, before: ref TNode) {.nimcall.} insertBefore*: proc (newNode, before: ref Node) {.nimcall.}
insertData*: proc (position: int, data: cstring) {.nimcall.} insertData*: proc (position: int, data: cstring) {.nimcall.}
removeAttribute*: proc (attr: cstring) {.nimcall.} removeAttribute*: proc (attr: cstring) {.nimcall.}
removeAttributeNode*: proc (attr: ref TNode) {.nimcall.} removeAttributeNode*: proc (attr: ref Node) {.nimcall.}
removeChild*: proc (child: ref TNode) {.nimcall.} removeChild*: proc (child: ref Node) {.nimcall.}
replaceChild*: proc (newNode, oldNode: ref TNode) {.nimcall.} replaceChild*: proc (newNode, oldNode: ref Node) {.nimcall.}
replaceData*: proc (start, len: int, text: cstring) {.nimcall.} replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
setAttribute*: proc (name, value: cstring) {.nimcall.} setAttribute*: proc (name, value: cstring) {.nimcall.}
setAttributeNode*: proc (attr: ref TNode) {.nimcall.} setAttributeNode*: proc (attr: ref Node) {.nimcall.}
{.deprecated: [TNode: Node, TNodeType: NodeType, TDocument: Document].}
when defined(kwin): when defined(kwin):
proc rawEcho {.compilerproc, asmNoStackFrame.} = proc rawEcho {.compilerproc, asmNoStackFrame.} =
@ -337,7 +340,7 @@ elif defined(nodejs):
else: else:
var var
document {.importc, nodecl.}: ref TDocument document {.importc, nodecl.}: ref Document
proc ewriteln(x: cstring) = proc ewriteln(x: cstring) =
var node = document.getElementsByTagName("body")[0] var node = document.getElementsByTagName("body")[0]

View file

@ -34,9 +34,10 @@ const
type type
PPointer = ptr pointer PPointer = ptr pointer
TByteArray = array[0..1000_0000, byte] ByteArray = array[0..1000_0000, byte]
PByte = ptr TByteArray PByte = ptr ByteArray
PString = ptr string PString = ptr string
{.deprecated: [TByteArray: ByteArray].}
# Page size of the system; in most cases 4096 bytes. For exotic OS or # Page size of the system; in most cases 4096 bytes. For exotic OS or
# CPU this needs to be changed: # CPU this needs to be changed:
@ -180,16 +181,17 @@ when defined(boehmgc):
dest[] = src dest[] = src
type type
TMemRegion = object {.final, pure.} MemRegion = object {.final, pure.}
{.deprecated: [TMemRegion: MemRegion].}
proc alloc(r: var TMemRegion, size: int): pointer = proc alloc(r: var MemRegion, size: int): pointer =
result = boehmAlloc(size) result = boehmAlloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc alloc0(r: var TMemRegion, size: int): pointer = proc alloc0(r: var MemRegion, size: int): pointer =
result = alloc(size) result = alloc(size)
zeroMem(result, size) zeroMem(result, size)
proc dealloc(r: var TMemRegion, p: pointer) = boehmDealloc(p) proc dealloc(r: var MemRegion, p: pointer) = boehmDealloc(p)
proc deallocOsPages(r: var TMemRegion) {.inline.} = discard proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard proc deallocOsPages() {.inline.} = discard
include "system/cellsets" include "system/cellsets"
@ -257,14 +259,15 @@ elif defined(nogc) and defined(useMalloc):
dest[] = src dest[] = src
type type
TMemRegion = object {.final, pure.} MemRegion = object {.final, pure.}
{.deprecated: [TMemRegion: MemRegion].}
proc alloc(r: var TMemRegion, size: int): pointer = proc alloc(r: var MemRegion, size: int): pointer =
result = alloc(size) result = alloc(size)
proc alloc0(r: var TMemRegion, size: int): pointer = proc alloc0(r: var MemRegion, size: int): pointer =
result = alloc0(size) result = alloc0(size)
proc dealloc(r: var TMemRegion, p: pointer) = dealloc(p) proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
proc deallocOsPages(r: var TMemRegion) {.inline.} = discard proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard proc deallocOsPages() {.inline.} = discard
elif defined(nogc): elif defined(nogc):
@ -313,7 +316,7 @@ elif defined(nogc):
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
dest[] = src dest[] = src
var allocator {.rtlThreadVar.}: TMemRegion var allocator {.rtlThreadVar.}: MemRegion
instantiateForRegion(allocator) instantiateForRegion(allocator)
include "system/cellsets" include "system/cellsets"
@ -323,7 +326,7 @@ else:
include "system/cellsets" include "system/cellsets"
when not leakDetector: when not leakDetector:
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell") sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
when compileOption("gc", "v2"): when compileOption("gc", "v2"):
include "system/gc2" include "system/gc2"
elif defined(gcMarkAndSweep): elif defined(gcMarkAndSweep):

View file

@ -19,10 +19,11 @@ const
MaxTraceLen = 20 # tracking the last 20 calls is enough MaxTraceLen = 20 # tracking the last 20 calls is enough
type type
TStackTrace* = array [0..MaxTraceLen-1, cstring] StackTrace* = array [0..MaxTraceLen-1, cstring]
TProfilerHook* = proc (st: TStackTrace) {.nimcall.} ProfilerHook* = proc (st: StackTrace) {.nimcall.}
{.deprecated: [TStackTrace: StackTrace, TProfilerHook: ProfilerHook].}
proc captureStackTrace(f: PFrame, st: var TStackTrace) = proc captureStackTrace(f: PFrame, st: var StackTrace) =
const const
firstCalls = 5 firstCalls = 5
var var
@ -51,14 +52,15 @@ proc captureStackTrace(f: PFrame, st: var TStackTrace) =
when defined(memProfiler): when defined(memProfiler):
type type
TMemProfilerHook* = proc (st: TStackTrace, requestedSize: int) {.nimcall, benign.} MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.}
{.deprecated: [TMemProfilerHook: MemProfilerHook].}
var var
profilerHook*: TMemProfilerHook profilerHook*: MemProfilerHook
## set this variable to provide a procedure that implements a profiler in ## set this variable to provide a procedure that implements a profiler in
## user space. See the `nimprof` module for a reference implementation. ## user space. See the `nimprof` module for a reference implementation.
proc callProfilerHook(hook: TMemProfilerHook, requestedSize: int) = proc callProfilerHook(hook: MemProfilerHook, requestedSize: int) =
var st: TStackTrace var st: StackTrace
captureStackTrace(framePtr, st) captureStackTrace(framePtr, st)
hook(st, requestedSize) hook(st, requestedSize)
@ -70,15 +72,15 @@ else:
SamplingInterval = 50_000 SamplingInterval = 50_000
# set this to change the default sampling interval # set this to change the default sampling interval
var var
profilerHook*: TProfilerHook profilerHook*: ProfilerHook
## set this variable to provide a procedure that implements a profiler in ## set this variable to provide a procedure that implements a profiler in
## user space. See the `nimprof` module for a reference implementation. ## user space. See the `nimprof` module for a reference implementation.
gTicker {.threadvar.}: int gTicker {.threadvar.}: int
proc callProfilerHook(hook: TProfilerHook) {.noinline.} = proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
# 'noinline' so that 'nimProfile' does not perform the stack allocation # 'noinline' so that 'nimProfile' does not perform the stack allocation
# in the common case. # in the common case.
var st: TStackTrace var st: StackTrace
captureStackTrace(framePtr, st) captureStackTrace(framePtr, st)
hook(st) hook(st)

View file

@ -121,38 +121,39 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerRtl.} =
reprSetAux(result, p, typ) reprSetAux(result, p, typ)
type type
TReprClosure {.final.} = object # we cannot use a global variable here ReprClosure {.final.} = object # we cannot use a global variable here
# as this wouldn't be thread-safe # as this wouldn't be thread-safe
when declared(TCellSet): when declared(CellSet):
marked: TCellSet marked: CellSet
recdepth: int # do not recurse endlessly recdepth: int # do not recurse endlessly
indent: int # indentation indent: int # indentation
{.deprecated: [TReprClosure: ReprClosure].}
when not defined(useNimRtl): when not defined(useNimRtl):
proc initReprClosure(cl: var TReprClosure) = proc initReprClosure(cl: var ReprClosure) =
# Important: cellsets does not lock the heap when doing allocations! We # Important: cellsets does not lock the heap when doing allocations! We
# have to do it here ... # have to do it here ...
when hasThreadSupport and hasSharedHeap and declared(heapLock): when hasThreadSupport and hasSharedHeap and declared(heapLock):
AcquireSys(HeapLock) AcquireSys(HeapLock)
when declared(TCellSet): when declared(CellSet):
init(cl.marked) init(cl.marked)
cl.recdepth = -1 # default is to display everything! cl.recdepth = -1 # default is to display everything!
cl.indent = 0 cl.indent = 0
proc deinitReprClosure(cl: var TReprClosure) = proc deinitReprClosure(cl: var ReprClosure) =
when declared(TCellSet): deinit(cl.marked) when declared(CellSet): deinit(cl.marked)
when hasThreadSupport and hasSharedHeap and declared(heapLock): when hasThreadSupport and hasSharedHeap and declared(heapLock):
ReleaseSys(HeapLock) ReleaseSys(HeapLock)
proc reprBreak(result: var string, cl: TReprClosure) = proc reprBreak(result: var string, cl: ReprClosure) =
add result, "\n" add result, "\n"
for i in 0..cl.indent-1: add result, ' ' for i in 0..cl.indent-1: add result, ' '
proc reprAux(result: var string, p: pointer, typ: PNimType, proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) {.benign.} cl: var ReprClosure) {.benign.}
proc reprArray(result: var string, p: pointer, typ: PNimType, proc reprArray(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
add result, "[" add result, "["
var bs = typ.base.size var bs = typ.base.size
for i in 0..typ.size div bs - 1: for i in 0..typ.size div bs - 1:
@ -161,7 +162,7 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprSequence(result: var string, p: pointer, typ: PNimType, proc reprSequence(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
if p == nil: if p == nil:
add result, "nil" add result, "nil"
return return
@ -174,7 +175,7 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode, proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
cl: var TReprClosure) {.benign.} = cl: var ReprClosure) {.benign.} =
case n.kind case n.kind
of nkNone: sysAssert(false, "reprRecordAux") of nkNone: sysAssert(false, "reprRecordAux")
of nkSlot: of nkSlot:
@ -191,7 +192,7 @@ when not defined(useNimRtl):
if m != nil: reprRecordAux(result, p, m, cl) if m != nil: reprRecordAux(result, p, m, cl)
proc reprRecord(result: var string, p: pointer, typ: PNimType, proc reprRecord(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
add result, "[" add result, "["
let oldLen = result.len let oldLen = result.len
reprRecordAux(result, p, typ.node, cl) reprRecordAux(result, p, typ.node, cl)
@ -201,9 +202,9 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprRef(result: var string, p: pointer, typ: PNimType, proc reprRef(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
# we know that p is not nil here: # we know that p is not nil here:
when declared(TCellSet): when declared(CellSet):
when defined(boehmGC) or defined(nogc): when defined(boehmGC) or defined(nogc):
var cell = cast[PCell](p) var cell = cast[PCell](p)
else: else:
@ -216,7 +217,7 @@ when not defined(useNimRtl):
reprAux(result, p, typ.base, cl) reprAux(result, p, typ.base, cl)
proc reprAux(result: var string, p: pointer, typ: PNimType, proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
if cl.recdepth == 0: if cl.recdepth == 0:
add result, "..." add result, "..."
return return
@ -261,7 +262,7 @@ when not defined(useNimRtl):
proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {. proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
compilerRtl.} = compilerRtl.} =
var var
cl: TReprClosure cl: ReprClosure
initReprClosure(cl) initReprClosure(cl)
result = "[" result = "["
var bs = elemtyp.size var bs = elemtyp.size
@ -274,7 +275,7 @@ proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
when not defined(useNimRtl): when not defined(useNimRtl):
proc reprAny(p: pointer, typ: PNimType): string = proc reprAny(p: pointer, typ: PNimType): string =
var var
cl: TReprClosure cl: ReprClosure
initReprClosure(cl) initReprClosure(cl)
result = "" result = ""
if typ.kind in {tyObject, tyTuple, tyArray, tyArrayConstr, tySet}: if typ.kind in {tyObject, tyTuple, tyArray, tyArrayConstr, tySet}:

View file

@ -10,7 +10,8 @@
# set handling # set handling
type type
TNimSet = array [0..4*2048-1, uint8] NimSet = array [0..4*2048-1, uint8]
{.deprecated: [TNimSet: NimSet].}
proc countBits32(n: int32): int {.compilerproc.} = proc countBits32(n: int32): int {.compilerproc.} =
var v = n var v = n
@ -22,7 +23,7 @@ proc countBits64(n: int64): int {.compilerproc.} =
result = countBits32(toU32(n and 0xffff'i64)) + result = countBits32(toU32(n and 0xffff'i64)) +
countBits32(toU32(n shr 16'i64)) countBits32(toU32(n shr 16'i64))
proc cardSet(s: TNimSet, len: int): int {.compilerproc.} = proc cardSet(s: NimSet, len: int): int {.compilerproc.} =
result = 0 result = 0
for i in countup(0, len-1): for i in countup(0, len-1):
inc(result, countBits32(int32(s[i]))) inc(result, countBits32(int32(s[i])))

View file

@ -11,8 +11,8 @@
when defined(Windows): when defined(Windows):
type type
THandle = int Handle = int
TSysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi SysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi
DebugInfo: pointer DebugInfo: pointer
LockCount: int32 LockCount: int32
RecursionCount: int32 RecursionCount: int32
@ -20,85 +20,87 @@ when defined(Windows):
LockSemaphore: int LockSemaphore: int
Reserved: int32 Reserved: int32
TSysCond = THandle SysCond = Handle
{.deprecated: [THandle: Handle, TSysLock: SysLock, TSysCond: SysCond].}
proc initSysLock(L: var TSysLock) {.stdcall, noSideEffect, proc initSysLock(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "InitializeCriticalSection".} dynlib: "kernel32", importc: "InitializeCriticalSection".}
## Initializes the lock `L`. ## Initializes the lock `L`.
proc tryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect, proc tryAcquireSysAux(L: var SysLock): int32 {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "TryEnterCriticalSection".} dynlib: "kernel32", importc: "TryEnterCriticalSection".}
## Tries to acquire the lock `L`. ## Tries to acquire the lock `L`.
proc tryAcquireSys(L: var TSysLock): bool {.inline.} = proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) != 0'i32 result = tryAcquireSysAux(L) != 0'i32
proc acquireSys(L: var TSysLock) {.stdcall, noSideEffect, proc acquireSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "EnterCriticalSection".} dynlib: "kernel32", importc: "EnterCriticalSection".}
## Acquires the lock `L`. ## Acquires the lock `L`.
proc releaseSys(L: var TSysLock) {.stdcall, noSideEffect, proc releaseSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "LeaveCriticalSection".} dynlib: "kernel32", importc: "LeaveCriticalSection".}
## Releases the lock `L`. ## Releases the lock `L`.
proc deinitSys(L: var TSysLock) {.stdcall, noSideEffect, proc deinitSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "DeleteCriticalSection".} dynlib: "kernel32", importc: "DeleteCriticalSection".}
proc createEvent(lpEventAttributes: pointer, proc createEvent(lpEventAttributes: pointer,
bManualReset, bInitialState: int32, bManualReset, bInitialState: int32,
lpName: cstring): TSysCond {.stdcall, noSideEffect, lpName: cstring): SysCond {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "CreateEventA".} dynlib: "kernel32", importc: "CreateEventA".}
proc closeHandle(hObject: THandle) {.stdcall, noSideEffect, proc closeHandle(hObject: Handle) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "CloseHandle".} dynlib: "kernel32", importc: "CloseHandle".}
proc waitForSingleObject(hHandle: THandle, dwMilliseconds: int32): int32 {. proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.} stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.}
proc signalSysCond(hEvent: TSysCond) {.stdcall, noSideEffect, proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "SetEvent".} dynlib: "kernel32", importc: "SetEvent".}
proc initSysCond(cond: var TSysCond) {.inline.} = proc initSysCond(cond: var SysCond) {.inline.} =
cond = createEvent(nil, 0'i32, 0'i32, nil) cond = createEvent(nil, 0'i32, 0'i32, nil)
proc deinitSysCond(cond: var TSysCond) {.inline.} = proc deinitSysCond(cond: var SysCond) {.inline.} =
closeHandle(cond) closeHandle(cond)
proc waitSysCond(cond: var TSysCond, lock: var TSysLock) = proc waitSysCond(cond: var SysCond, lock: var SysLock) =
releaseSys(lock) releaseSys(lock)
discard waitForSingleObject(cond, -1'i32) discard waitForSingleObject(cond, -1'i32)
acquireSys(lock) acquireSys(lock)
proc waitSysCondWindows(cond: var TSysCond) = proc waitSysCondWindows(cond: var SysCond) =
discard waitForSingleObject(cond, -1'i32) discard waitForSingleObject(cond, -1'i32)
else: else:
type type
TSysLock {.importc: "pthread_mutex_t", pure, final, SysLock {.importc: "pthread_mutex_t", pure, final,
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
TSysCond {.importc: "pthread_cond_t", pure, final, SysCond {.importc: "pthread_cond_t", pure, final,
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
proc initSysLock(L: var TSysLock, attr: pointer = nil) {. proc initSysLock(L: var SysLock, attr: pointer = nil) {.
importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
proc acquireSys(L: var TSysLock) {.noSideEffect, proc acquireSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_lock", header: "<pthread.h>".} importc: "pthread_mutex_lock", header: "<pthread.h>".}
proc tryAcquireSysAux(L: var TSysLock): cint {.noSideEffect, proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "<pthread.h>".} importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc tryAcquireSys(L: var TSysLock): bool {.inline.} = proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) == 0'i32 result = tryAcquireSysAux(L) == 0'i32
proc releaseSys(L: var TSysLock) {.noSideEffect, proc releaseSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_unlock", header: "<pthread.h>".} importc: "pthread_mutex_unlock", header: "<pthread.h>".}
proc deinitSys(L: var TSysLock) {.noSideEffect, proc deinitSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_destroy", header: "<pthread.h>".} importc: "pthread_mutex_destroy", header: "<pthread.h>".}
proc initSysCond(cond: var TSysCond, cond_attr: pointer = nil) {. proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
proc waitSysCond(cond: var TSysCond, lock: var TSysLock) {. proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
proc signalSysCond(cond: var TSysCond) {. proc signalSysCond(cond: var SysCond) {.
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
proc deinitSysCond(cond: var TSysCond) {.noSideEffect, proc deinitSysCond(cond: var SysCond) {.noSideEffect,
importc: "pthread_cond_destroy", header: "<pthread.h>".} importc: "pthread_cond_destroy", header: "<pthread.h>".}

View file

@ -19,9 +19,9 @@ when not declared(NimString):
type type
CondVar = object CondVar = object
c: TSysCond c: SysCond
when defined(posix): when defined(posix):
stupidLock: TSysLock stupidLock: SysLock
counter: int counter: int
proc createCondVar(): CondVar = proc createCondVar(): CondVar =

View file

@ -24,8 +24,8 @@
## import locks ## import locks
## ##
## var ## var
## thr: array [0..4, TThread[tuple[a,b: int]]] ## thr: array [0..4, Thread[tuple[a,b: int]]]
## L: TLock ## L: Lock
## ##
## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = ## proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
## for i in interval.a..interval.b: ## for i in interval.a..interval.b:
@ -51,40 +51,41 @@ const
when defined(windows): when defined(windows):
type type
TSysThread = THandle SysThread = Handle
TWinThreadProc = proc (x: pointer): int32 {.stdcall.} WinThreadProc = proc (x: pointer): int32 {.stdcall.}
{.deprecated: [TSysThread: SysThread, TWinThreadProc: WinThreadProc].}
proc createThread(lpThreadAttributes: pointer, dwStackSize: int32, proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
lpStartAddress: TWinThreadProc, lpStartAddress: WinThreadProc,
lpParameter: pointer, lpParameter: pointer,
dwCreationFlags: int32, dwCreationFlags: int32,
lpThreadId: var int32): TSysThread {. lpThreadId: var int32): SysThread {.
stdcall, dynlib: "kernel32", importc: "CreateThread".} stdcall, dynlib: "kernel32", importc: "CreateThread".}
proc winSuspendThread(hThread: TSysThread): int32 {. proc winSuspendThread(hThread: SysThread): int32 {.
stdcall, dynlib: "kernel32", importc: "SuspendThread".} stdcall, dynlib: "kernel32", importc: "SuspendThread".}
proc winResumeThread(hThread: TSysThread): int32 {. proc winResumeThread(hThread: SysThread): int32 {.
stdcall, dynlib: "kernel32", importc: "ResumeThread".} stdcall, dynlib: "kernel32", importc: "ResumeThread".}
proc waitForMultipleObjects(nCount: int32, proc waitForMultipleObjects(nCount: int32,
lpHandles: ptr TSysThread, lpHandles: ptr SysThread,
bWaitAll: int32, bWaitAll: int32,
dwMilliseconds: int32): int32 {. dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
proc terminateThread(hThread: TSysThread, dwExitCode: int32): int32 {. proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "TerminateThread".} stdcall, dynlib: "kernel32", importc: "TerminateThread".}
type type
TThreadVarSlot = distinct int32 ThreadVarSlot = distinct int32
when true: when true:
proc threadVarAlloc(): TThreadVarSlot {. proc threadVarAlloc(): ThreadVarSlot {.
importc: "TlsAlloc", stdcall, header: "<windows.h>".} importc: "TlsAlloc", stdcall, header: "<windows.h>".}
proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, header: "<windows.h>".} importc: "TlsSetValue", stdcall, header: "<windows.h>".}
proc tlsGetValue(dwTlsIndex: TThreadVarSlot): pointer {. proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
importc: "TlsGetValue", stdcall, header: "<windows.h>".} importc: "TlsGetValue", stdcall, header: "<windows.h>".}
proc getLastError(): uint32 {. proc getLastError(): uint32 {.
@ -92,16 +93,16 @@ when defined(windows):
proc setLastError(x: uint32) {. proc setLastError(x: uint32) {.
importc: "SetLastError", stdcall, header: "<windows.h>".} importc: "SetLastError", stdcall, header: "<windows.h>".}
proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer = proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
let realLastError = getLastError() let realLastError = getLastError()
result = tlsGetValue(dwTlsIndex) result = tlsGetValue(dwTlsIndex)
setLastError(realLastError) setLastError(realLastError)
else: else:
proc threadVarAlloc(): TThreadVarSlot {. proc threadVarAlloc(): ThreadVarSlot {.
importc: "TlsAlloc", stdcall, dynlib: "kernel32".} importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, dynlib: "kernel32".} importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer {. proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".} importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
else: else:
@ -111,55 +112,58 @@ else:
{.passC: "-pthread".} {.passC: "-pthread".}
type type
TSysThread {.importc: "pthread_t", header: "<sys/types.h>", SysThread {.importc: "pthread_t", header: "<sys/types.h>",
final, pure.} = object final, pure.} = object
Tpthread_attr {.importc: "pthread_attr_t", Pthread_attr {.importc: "pthread_attr_t",
header: "<sys/types.h>", final, pure.} = object header: "<sys/types.h>", final, pure.} = object
Ttimespec {.importc: "struct timespec", Timespec {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object header: "<time.h>", final, pure.} = object
tv_sec: int tv_sec: int
tv_nsec: int tv_nsec: int
{.deprecated: [TSysThread: SysThread, Tpthread_attr: PThreadAttr,
Ttimespec: Timespec].}
proc pthread_attr_init(a1: var TPthread_attr) {. proc pthread_attr_init(a1: var PthreadAttr) {.
importc, header: "<pthread.h>".} importc, header: "<pthread.h>".}
proc pthread_attr_setstacksize(a1: var TPthread_attr, a2: int) {. proc pthread_attr_setstacksize(a1: var PthreadAttr, a2: int) {.
importc, header: "<pthread.h>".} importc, header: "<pthread.h>".}
proc pthread_create(a1: var TSysThread, a2: var TPthread_attr, proc pthread_create(a1: var SysThread, a2: var PthreadAttr,
a3: proc (x: pointer): pointer {.noconv.}, a3: proc (x: pointer): pointer {.noconv.},
a4: pointer): cint {.importc: "pthread_create", a4: pointer): cint {.importc: "pthread_create",
header: "<pthread.h>".} header: "<pthread.h>".}
proc pthread_join(a1: TSysThread, a2: ptr pointer): cint {. proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
importc, header: "<pthread.h>".} importc, header: "<pthread.h>".}
proc pthread_cancel(a1: TSysThread): cint {. proc pthread_cancel(a1: SysThread): cint {.
importc: "pthread_cancel", header: "<pthread.h>".} importc: "pthread_cancel", header: "<pthread.h>".}
type type
TThreadVarSlot {.importc: "pthread_key_t", pure, final, ThreadVarSlot {.importc: "pthread_key_t", pure, final,
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
{.deprecated: [TThreadVarSlot: ThreadVarSlot].}
proc pthread_getspecific(a1: TThreadVarSlot): pointer {. proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
importc: "pthread_getspecific", header: "<pthread.h>".} importc: "pthread_getspecific", header: "<pthread.h>".}
proc pthread_key_create(a1: ptr TThreadVarSlot, proc pthread_key_create(a1: ptr ThreadVarSlot,
destruct: proc (x: pointer) {.noconv.}): int32 {. destruct: proc (x: pointer) {.noconv.}): int32 {.
importc: "pthread_key_create", header: "<pthread.h>".} importc: "pthread_key_create", header: "<pthread.h>".}
proc pthread_key_delete(a1: TThreadVarSlot): int32 {. proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
importc: "pthread_key_delete", header: "<pthread.h>".} importc: "pthread_key_delete", header: "<pthread.h>".}
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {. proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".} importc: "pthread_setspecific", header: "<pthread.h>".}
proc threadVarAlloc(): TThreadVarSlot {.inline.} = proc threadVarAlloc(): ThreadVarSlot {.inline.} =
discard pthread_key_create(addr(result), nil) discard pthread_key_create(addr(result), nil)
proc threadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} = proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
discard pthread_setspecific(s, value) discard pthread_setspecific(s, value)
proc threadVarGetValue(s: TThreadVarSlot): pointer {.inline.} = proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
result = pthread_getspecific(s) result = pthread_getspecific(s)
when useStackMaskHack: when useStackMaskHack:
proc pthread_attr_setstack(attr: var TPthread_attr, stackaddr: pointer, proc pthread_attr_setstack(attr: var PthreadAttr, stackaddr: pointer,
size: int): cint {. size: int): cint {.
importc: "pthread_attr_setstack", header: "<pthread.h>".} importc: "pthread_attr_setstack", header: "<pthread.h>".}
@ -175,13 +179,13 @@ when emulatedThreadVars:
# allocations are needed. Currently less than 7K are used on a 64bit machine. # allocations are needed. Currently less than 7K are used on a 64bit machine.
# We use ``float`` for proper alignment: # We use ``float`` for proper alignment:
type type
TThreadLocalStorage = array [0..1_000, float] ThreadLocalStorage = array [0..1_000, float]
PGcThread = ptr TGcThread PGcThread = ptr GcThread
TGcThread {.pure, inheritable.} = object GcThread {.pure, inheritable.} = object
sys: TSysThread sys: SysThread
when emulatedThreadVars and not useStackMaskHack: when emulatedThreadVars and not useStackMaskHack:
tls: TThreadLocalStorage tls: ThreadLocalStorage
else: else:
nil nil
when hasSharedHeap: when hasSharedHeap:
@ -190,15 +194,16 @@ type
stackSize: int stackSize: int
else: else:
nil nil
{.deprecated: [TThreadLocalStorage: ThreadLocalStorage, TGcThread: GcThread].}
# XXX it'd be more efficient to not use a global variable for the # XXX it'd be more efficient to not use a global variable for the
# thread storage slot, but to rely on the implementation to assign slot X # thread storage slot, but to rely on the implementation to assign slot X
# for us... ;-) # for us... ;-)
var globalsSlot: TThreadVarSlot var globalsSlot: ThreadVarSlot
when not defined(useNimRtl): when not defined(useNimRtl):
when not useStackMaskHack: when not useStackMaskHack:
var mainThread: TGcThread var mainThread: GcThread
proc initThreadVarsEmulation() {.compilerProc, inline.} = proc initThreadVarsEmulation() {.compilerProc, inline.} =
when not defined(useNimRtl): when not defined(useNimRtl):
@ -206,7 +211,7 @@ proc initThreadVarsEmulation() {.compilerProc, inline.} =
when declared(mainThread): when declared(mainThread):
threadVarSetValue(globalsSlot, addr(mainThread)) threadVarSetValue(globalsSlot, addr(mainThread))
#const globalsSlot = TThreadVarSlot(0) #const globalsSlot = ThreadVarSlot(0)
#sysAssert checkSlot.int == globalsSlot.int #sysAssert checkSlot.int == globalsSlot.int
when emulatedThreadVars: when emulatedThreadVars:
@ -228,7 +233,7 @@ when not defined(useNimRtl):
initGC() initGC()
when emulatedThreadVars: when emulatedThreadVars:
if nimThreadVarsSize() > sizeof(TThreadLocalStorage): if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
echo "too large thread local storage size requested" echo "too large thread local storage size requested"
quit 1 quit 1
@ -269,26 +274,27 @@ when not defined(useNimRtl):
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. # use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
type type
TThread* {.pure, final.}[TArg] = Thread* {.pure, final.}[TArg] =
object of TGcThread ## Nim thread. A thread is a heavy object (~14K) object of GcThread ## Nim thread. A thread is a heavy object (~14K)
## that **must not** be part of a message! Use ## that **must not** be part of a message! Use
## a ``TThreadId`` for that. ## a ``ThreadId`` for that.
when TArg is void: when TArg is void:
dataFn: proc () {.nimcall, gcsafe.} dataFn: proc () {.nimcall, gcsafe.}
else: else:
dataFn: proc (m: TArg) {.nimcall, gcsafe.} dataFn: proc (m: TArg) {.nimcall, gcsafe.}
data: TArg data: TArg
TThreadId*[TArg] = ptr TThread[TArg] ## the current implementation uses ThreadId*[TArg] = ptr Thread[TArg] ## the current implementation uses
## a pointer as a thread ID. ## a pointer as a thread ID.
{.deprecated: [TThread: Thread, TThreadId: ThreadId].}
when not defined(boehmgc) and not hasSharedHeap: when not defined(boehmgc) and not hasSharedHeap:
proc deallocOsPages() proc deallocOsPages()
template threadProcWrapperBody(closure: expr) {.immediate.} = template threadProcWrapperBody(closure: expr) {.immediate.} =
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
var t = cast[ptr TThread[TArg]](closure) var t = cast[ptr Thread[TArg]](closure)
when useStackMaskHack: when useStackMaskHack:
var tls: TThreadLocalStorage var tls: ThreadLocalStorage
when not defined(boehmgc) and not defined(nogc) and not hasSharedHeap: when not defined(boehmgc) and not defined(nogc) and not hasSharedHeap:
# init the GC for this thread: # init the GC for this thread:
setStackBottom(addr(t)) setStackBottom(addr(t))
@ -319,35 +325,35 @@ else:
threadProcWrapperBody(closure) threadProcWrapperBody(closure)
{.pop.} {.pop.}
proc running*[TArg](t: TThread[TArg]): bool {.inline.} = proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
## returns true if `t` is running. ## returns true if `t` is running.
result = t.dataFn != nil result = t.dataFn != nil
when hostOS == "windows": when hostOS == "windows":
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} = proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish. ## waits for the thread `t` to finish.
discard waitForSingleObject(t.sys, -1'i32) discard waitForSingleObject(t.sys, -1'i32)
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) = proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish. ## waits for every thread in `t` to finish.
var a: array[0..255, TSysThread] var a: array[0..255, SysThread]
sysAssert a.len >= t.len, "a.len >= t.len" sysAssert a.len >= t.len, "a.len >= t.len"
for i in 0..t.high: a[i] = t[i].sys for i in 0..t.high: a[i] = t[i].sys
discard waitForMultipleObjects(t.len.int32, discard waitForMultipleObjects(t.len.int32,
cast[ptr TSysThread](addr(a)), 1, -1) cast[ptr SysThread](addr(a)), 1, -1)
else: else:
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} = proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish. ## waits for the thread `t` to finish.
discard pthread_join(t.sys, nil) discard pthread_join(t.sys, nil)
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) = proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish. ## waits for every thread in `t` to finish.
for i in 0..t.high: joinThread(t[i]) for i in 0..t.high: joinThread(t[i])
when false: when false:
# XXX a thread should really release its heap here somehow: # XXX a thread should really release its heap here somehow:
proc destroyThread*[TArg](t: var TThread[TArg]) = proc destroyThread*[TArg](t: var Thread[TArg]) =
## forces the thread `t` to terminate. This is potentially dangerous if ## forces the thread `t` to terminate. This is potentially dangerous if
## you don't have full control over `t` and its acquired resources. ## you don't have full control over `t` and its acquired resources.
when hostOS == "windows": when hostOS == "windows":
@ -358,7 +364,7 @@ when false:
t.dataFn = nil t.dataFn = nil
when hostOS == "windows": when hostOS == "windows":
proc createThread*[TArg](t: var TThread[TArg], proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread.}, tp: proc (arg: TArg) {.thread.},
param: TArg) = param: TArg) =
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
@ -373,7 +379,7 @@ when hostOS == "windows":
if t.sys <= 0: if t.sys <= 0:
raise newException(ResourceExhaustedError, "cannot create thread") raise newException(ResourceExhaustedError, "cannot create thread")
else: else:
proc createThread*[TArg](t: var TThread[TArg], proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread.}, tp: proc (arg: TArg) {.thread.},
param: TArg) = param: TArg) =
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
@ -382,29 +388,29 @@ else:
when TArg isnot void: t.data = param when TArg isnot void: t.data = param
t.dataFn = tp t.dataFn = tp
when hasSharedHeap: t.stackSize = ThreadStackSize when hasSharedHeap: t.stackSize = ThreadStackSize
var a {.noinit.}: Tpthread_attr var a {.noinit.}: PthreadAttr
pthread_attr_init(a) pthread_attr_init(a)
pthread_attr_setstacksize(a, ThreadStackSize) pthread_attr_setstacksize(a, ThreadStackSize)
if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0:
raise newException(ResourceExhaustedError, "cannot create thread") raise newException(ResourceExhaustedError, "cannot create thread")
proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} = proc threadId*[TArg](t: var Thread[TArg]): ThreadId[TArg] {.inline.} =
## returns the thread ID of `t`. ## returns the thread ID of `t`.
result = addr(t) result = addr(t)
proc myThreadId*[TArg](): TThreadId[TArg] = proc myThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the thread that calls this proc. This is unsafe ## returns the thread ID of the thread that calls this proc. This is unsafe
## because the type ``TArg`` is not checked for consistency! ## because the type ``TArg`` is not checked for consistency!
result = cast[TThreadId[TArg]](threadVarGetValue(globalsSlot)) result = cast[ThreadId[TArg]](threadVarGetValue(globalsSlot))
when false: when false:
proc mainThreadId*[TArg](): TThreadId[TArg] = proc mainThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the main thread. ## returns the thread ID of the main thread.
result = cast[TThreadId[TArg]](addr(mainThread)) result = cast[ThreadId[TArg]](addr(mainThread))
when useStackMaskHack: when useStackMaskHack:
proc runMain(tp: proc () {.thread.}) {.compilerproc.} = proc runMain(tp: proc () {.thread.}) {.compilerproc.} =
var mainThread: TThread[pointer] var mainThread: Thread[pointer]
createThread(mainThread, tp) createThread(mainThread, tp)
joinThread(mainThread) joinThread(mainThread)

View file

@ -11,83 +11,86 @@
## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_ ## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_
type type
TTicks = distinct int64 Ticks = distinct int64
TNanos = int64 Nanos = int64
{.deprecated: [TTicks: Ticks, TNanos: Nanos].}
when defined(windows): when defined(windows):
proc QueryPerformanceCounter(res: var TTicks) {. proc QueryPerformanceCounter(res: var Ticks) {.
importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".} importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".}
proc QueryPerformanceFrequency(res: var int64) {. proc QueryPerformanceFrequency(res: var int64) {.
importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".} importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".}
proc getTicks(): TTicks {.inline.} = proc getTicks(): Ticks {.inline.} =
QueryPerformanceCounter(result) QueryPerformanceCounter(result)
proc `-`(a, b: TTicks): TNanos = proc `-`(a, b: Ticks): Nanos =
var frequency: int64 var frequency: int64
QueryPerformanceFrequency(frequency) QueryPerformanceFrequency(frequency)
var performanceCounterRate = 1e+9'f64 / float64(frequency) var performanceCounterRate = 1e+9'f64 / float64(frequency)
result = TNanos(float64(a.int64 - b.int64) * performanceCounterRate) result = Nanos(float64(a.int64 - b.int64) * performanceCounterRate)
elif defined(macosx): elif defined(macosx):
type type
TMachTimebaseInfoData {.pure, final, MachTimebaseInfoData {.pure, final,
importc: "mach_timebase_info_data_t", importc: "mach_timebase_info_data_t",
header: "<mach/mach_time.h>".} = object header: "<mach/mach_time.h>".} = object
numer, denom: int32 numer, denom: int32
{.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".} proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".}
proc mach_timebase_info(info: var TMachTimebaseInfoData) {.importc, proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc,
header: "<mach/mach_time.h>".} header: "<mach/mach_time.h>".}
proc getTicks(): TTicks {.inline.} = proc getTicks(): Ticks {.inline.} =
result = TTicks(mach_absolute_time()) result = Ticks(mach_absolute_time())
var timeBaseInfo: TMachTimebaseInfoData var timeBaseInfo: MachTimebaseInfoData
mach_timebase_info(timeBaseInfo) mach_timebase_info(timeBaseInfo)
proc `-`(a, b: TTicks): TNanos = proc `-`(a, b: Ticks): Nanos =
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
elif defined(posixRealtime): elif defined(posixRealtime):
type type
TClockid {.importc: "clockid_t", header: "<time.h>", final.} = object Clockid {.importc: "clockid_t", header: "<time.h>", final.} = object
TTimeSpec {.importc: "struct timespec", header: "<time.h>", TimeSpec {.importc: "struct timespec", header: "<time.h>",
final, pure.} = object ## struct timespec final, pure.} = object ## struct timespec
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_nsec: int ## Nanoseconds. tv_nsec: int ## Nanoseconds.
{.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
var var
CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: TClockid CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
proc clock_gettime(clkId: TClockid, tp: var TTimespec) {. proc clock_gettime(clkId: Clockid, tp: var Timespec) {.
importc: "clock_gettime", header: "<time.h>".} importc: "clock_gettime", header: "<time.h>".}
proc getTicks(): TTicks = proc getTicks(): Ticks =
var t: TTimespec var t: Timespec
clock_gettime(CLOCK_REALTIME, t) clock_gettime(CLOCK_REALTIME, t)
result = TTicks(int64(t.tv_sec) * 1000000000'i64 + int64(t.tv_nsec)) result = Ticks(int64(t.tv_sec) * 1000000000'i64 + int64(t.tv_nsec))
proc `-`(a, b: TTicks): TNanos {.borrow.} proc `-`(a, b: Ticks): Nanos {.borrow.}
else: else:
# fallback Posix implementation: # fallback Posix implementation:
type type
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>", Timeval {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval final, pure.} = object ## struct timeval
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_usec: int ## Microseconds. tv_usec: int ## Microseconds.
{.deprecated: [Ttimeval: Timeval].}
proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {. proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>".} importc: "gettimeofday", header: "<sys/time.h>".}
proc getTicks(): TTicks = proc getTicks(): Ticks =
var t: Ttimeval var t: Timeval
posix_gettimeofday(t) posix_gettimeofday(t)
result = TTicks(int64(t.tv_sec) * 1000_000_000'i64 + result = Ticks(int64(t.tv_sec) * 1000_000_000'i64 +
int64(t.tv_usec) * 1000'i64) int64(t.tv_usec) * 1000'i64)
proc `-`(a, b: TTicks): TNanos {.borrow.} proc `-`(a, b: Ticks): Nanos {.borrow.}

View file

@ -14,8 +14,9 @@ when not declared(NimString):
{.error: "You must not import this module explicitly".} {.error: "You must not import this module explicitly".}
type type
TUtf16Char* = distinct int16 Utf16Char* = distinct int16
WideCString* = ref array[0.. 1_000_000, TUtf16Char] WideCString* = ref array[0.. 1_000_000, Utf16Char]
{.deprecated: [TUtf16Char: Utf16Char].}
proc len*(w: WideCString): int = proc len*(w: WideCString): int =
## returns the length of a widestring. This traverses the whole string to ## returns the length of a widestring. This traverses the whole string to
@ -23,7 +24,7 @@ proc len*(w: WideCString): int =
while int16(w[result]) != 0'i16: inc result while int16(w[result]) != 0'i16: inc result
const const
UNI_REPLACEMENT_CHAR = TUtf16Char(0xFFFD'i16) UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
UNI_MAX_BMP = 0x0000FFFF UNI_MAX_BMP = 0x0000FFFF
UNI_MAX_UTF16 = 0x0010FFFF UNI_MAX_UTF16 = 0x0010FFFF
UNI_MAX_UTF32 = 0x7FFFFFFF UNI_MAX_UTF32 = 0x7FFFFFFF
@ -89,16 +90,16 @@ proc newWideCString*(source: cstring, L: int): WideCString =
if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END: if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
result[d] = UNI_REPLACEMENT_CHAR result[d] = UNI_REPLACEMENT_CHAR
else: else:
result[d] = TUtf16Char(toU16(ch)) result[d] = Utf16Char(toU16(ch))
elif ch >% UNI_MAX_UTF16: elif ch >% UNI_MAX_UTF16:
result[d] = UNI_REPLACEMENT_CHAR result[d] = UNI_REPLACEMENT_CHAR
else: else:
let ch = ch -% halfBase let ch = ch -% halfBase
result[d] = TUtf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START)) result[d] = Utf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START))
inc d inc d
result[d] = TUtf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START)) result[d] = Utf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
inc d inc d
result[d] = TUtf16Char(0'i16) result[d] = Utf16Char(0'i16)
proc newWideCString*(s: cstring): WideCString = proc newWideCString*(s: cstring): WideCString =
if s.isNil: return nil if s.isNil: return nil

View file

@ -29,12 +29,12 @@ import
type type
MMRESULT* = uint32 MMRESULT* = uint32
MMVERSION* = uint32 MMVERSION* = uint32
HWAVEOUT* = THandle HWAVEOUT* = Handle
LPHWAVEOUT* = ptr HWAVEOUT LPHWAVEOUT* = ptr HWAVEOUT
HWAVEIN* = THandle HWAVEIN* = Handle
LPHWAVEIN* = ptr HWAVEOUT LPHWAVEIN* = ptr HWAVEOUT
HWAVE* = THandle HWAVE* = Handle
LPHWAVE* = ptr THandle LPHWAVE* = ptr Handle
LPUINT* = ptr uint32 LPUINT* = ptr uint32
const const
@ -151,7 +151,7 @@ const
CALLBACK_FUNCTION* = 0x00030000 CALLBACK_FUNCTION* = 0x00030000
type type
HDRVR* = THandle HDRVR* = Handle
const const
DRV_LOAD* = 1 DRV_LOAD* = 1
@ -178,7 +178,7 @@ const
DRV_MCI_LAST* = (DRV_RESERVED + 0x00000FFF) DRV_MCI_LAST* = (DRV_RESERVED + 0x00000FFF)
type type
PDRVCALLBACK* = proc (hdrvr: THandle, uMsg: uint32, dwUser, dw1, dw2: DWORD){. PDRVCALLBACK* = proc (hdrvr: Handle, uMsg: uint32, dwUser, dw1, dw2: DWORD){.
stdcall.} stdcall.}
proc sndPlaySoundA*(Name: LPCSTR, flags: uint32): bool{.stdcall, proc sndPlaySoundA*(Name: LPCSTR, flags: uint32): bool{.stdcall,
@ -991,7 +991,6 @@ type
NPMMTIME* = ptr MMTIME NPMMTIME* = ptr MMTIME
LPMMTIME* = ptr MMTIME LPMMTIME* = ptr MMTIME
PWAVEHDR* = ptr WAVEHDR PWAVEHDR* = ptr WAVEHDR
TMMTime* = MMTIME
WAVEHDR* {.final.} = object WAVEHDR* {.final.} = object
lpData*: cstring lpData*: cstring
dwBufferLength*: DWORD dwBufferLength*: DWORD
@ -1002,7 +1001,6 @@ type
lpNext*: PWAVEHDR lpNext*: PWAVEHDR
reserved*: DWORD reserved*: DWORD
TWAVEHDR* = WAVEHDR
NPWAVEHDR* = ptr WAVEHDR NPWAVEHDR* = ptr WAVEHDR
LPWAVEHDR* = ptr WAVEHDR LPWAVEHDR* = ptr WAVEHDR
WAVEOUTCAPSA* {.final.} = object WAVEOUTCAPSA* {.final.} = object
@ -1015,7 +1013,6 @@ type
wReserved1*: int16 wReserved1*: int16
dwSupport*: DWORD dwSupport*: DWORD
TWAVEOUTCAPSA* = WAVEOUTCAPSA
PWAVEOUTCAPSA* = ptr WAVEOUTCAPSA PWAVEOUTCAPSA* = ptr WAVEOUTCAPSA
NPWAVEOUTCAPSA* = ptr WAVEOUTCAPSA NPWAVEOUTCAPSA* = ptr WAVEOUTCAPSA
LPWAVEOUTCAPSA* = ptr WAVEOUTCAPSA LPWAVEOUTCAPSA* = ptr WAVEOUTCAPSA
@ -1032,7 +1029,8 @@ type
PWAVEOUTCAPSW* = ptr WAVEOUTCAPSW PWAVEOUTCAPSW* = ptr WAVEOUTCAPSW
NPWAVEOUTCAPSW* = ptr WAVEOUTCAPSW NPWAVEOUTCAPSW* = ptr WAVEOUTCAPSW
LPWAVEOUTCAPSW* = ptr WAVEOUTCAPSW LPWAVEOUTCAPSW* = ptr WAVEOUTCAPSW
TWAVEOUTCAPSW* = WAVEOUTCAPSW {.deprecated: [Twavehdr: Wavehdr, Tmmtime: Mmtime, Twaveoutcapsa: Waveoutcapsa,
Twaveoutcapsw: Waveoutcapsw].}
when defined(UNICODE): when defined(UNICODE):
type type
@ -1127,10 +1125,10 @@ type
LPWAVEFORMATEX* = ptr WAVEFORMATEX LPWAVEFORMATEX* = ptr WAVEFORMATEX
LPCWAVEFORMATEX* = ptr WAVEFORMATEX LPCWAVEFORMATEX* = ptr WAVEFORMATEX
TWAVEFORMATEX* = WAVEFORMATEX TWAVEFORMATEX* = WAVEFORMATEX
HMIDI* = THandle HMIDI* = Handle
HMIDIIN* = THandle HMIDIIN* = Handle
HMIDIOUT* = THandle HMIDIOUT* = Handle
HMIDISTRM* = THandle HMIDISTRM* = Handle
LPHMIDI* = ptr HMIDI LPHMIDI* = ptr HMIDI
LPHMIDIIN* = ptr HMIDIIN LPHMIDIIN* = ptr HMIDIIN
LPHMIDIOUT* = ptr HMIDIOUT LPHMIDIOUT* = ptr HMIDIOUT
@ -1299,9 +1297,9 @@ else:
LPAUXCAPS* = LPAUXCAPSA LPAUXCAPS* = LPAUXCAPSA
type type
TAUXCAPS* = AUXCAPS TAUXCAPS* = AUXCAPS
HMIXEROBJ* = THandle HMIXEROBJ* = Handle
LPHMIXEROBJ* = ptr HMIXEROBJ LPHMIXEROBJ* = ptr HMIXEROBJ
HMIXER* = THandle HMIXER* = Handle
LPHMIXER* = ptr HMIXER LPHMIXER* = ptr HMIXER
proc mixerGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", proc mixerGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll",
@ -1647,7 +1645,7 @@ type
TJOYINFOEX* = JOYINFOEX TJOYINFOEX* = JOYINFOEX
FOURCC* = DWORD FOURCC* = DWORD
HPSTR* = cstring HPSTR* = cstring
HMMIO* = THandle HMMIO* = Handle
LPMMIOPROC* = proc (x1: LPSTR, x2: uint32, x3, x4: LPARAM): LRESULT{.stdcall.} LPMMIOPROC* = proc (x1: LPSTR, x2: uint32, x3, x4: LPARAM): LRESULT{.stdcall.}
TMMIOPROC* = LPMMIOPROC TMMIOPROC* = LPMMIOPROC
MMIOINFO* {.final.} = object MMIOINFO* {.final.} = object
@ -2096,14 +2094,14 @@ else:
type type
MCI_ANIM_RECT_PARMS* {.final.} = object MCI_ANIM_RECT_PARMS* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
rc*: TRECT rc*: RECT
PMCI_ANIM_RECT_PARMS* = ptr MCI_ANIM_RECT_PARMS PMCI_ANIM_RECT_PARMS* = ptr MCI_ANIM_RECT_PARMS
LPMCI_ANIM_RECT_PARMS* = ptr MCI_ANIM_RECT_PARMS LPMCI_ANIM_RECT_PARMS* = ptr MCI_ANIM_RECT_PARMS
TMCI_ANIM_RECT_PARMS* = MCI_ANIM_RECT_PARMS TMCI_ANIM_RECT_PARMS* = MCI_ANIM_RECT_PARMS
MCI_ANIM_UPDATE_PARMS* {.final.} = object MCI_ANIM_UPDATE_PARMS* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
rc*: TRECT rc*: RECT
hDC*: HDC hDC*: HDC
PMCI_ANIM_UPDATE_PARMS* = ptr MCI_ANIM_UPDATE_PARMS PMCI_ANIM_UPDATE_PARMS* = ptr MCI_ANIM_UPDATE_PARMS
@ -2179,7 +2177,7 @@ type
TMCI_OVLY_WINDOW_PARMS* = MCI_OVLY_WINDOW_PARMSW TMCI_OVLY_WINDOW_PARMS* = MCI_OVLY_WINDOW_PARMSW
MCI_OVLY_RECT_PARMS* {.final.} = object MCI_OVLY_RECT_PARMS* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
rc*: TRECT rc*: RECT
PMCI_OVLY_RECT_PARMS* = ptr MCI_OVLY_RECT_PARMS PMCI_OVLY_RECT_PARMS* = ptr MCI_OVLY_RECT_PARMS
LPMCI_OVLY_RECT_PARMS* = ptr MCI_OVLY_RECT_PARMS LPMCI_OVLY_RECT_PARMS* = ptr MCI_OVLY_RECT_PARMS
@ -2187,7 +2185,7 @@ type
MCI_OVLY_SAVE_PARMSA* {.final.} = object MCI_OVLY_SAVE_PARMSA* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
lpfilename*: LPCSTR lpfilename*: LPCSTR
rc*: TRECT rc*: RECT
PMCI_OVLY_SAVE_PARMSA* = ptr MCI_OVLY_SAVE_PARMSA PMCI_OVLY_SAVE_PARMSA* = ptr MCI_OVLY_SAVE_PARMSA
LPMCI_OVLY_SAVE_PARMSA* = ptr MCI_OVLY_SAVE_PARMSA LPMCI_OVLY_SAVE_PARMSA* = ptr MCI_OVLY_SAVE_PARMSA
@ -2195,7 +2193,7 @@ type
MCI_OVLY_SAVE_PARMSW* {.final.} = object MCI_OVLY_SAVE_PARMSW* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
lpfilename*: LPCWSTR lpfilename*: LPCWSTR
rc*: TRECT rc*: RECT
PMCI_OVLY_SAVE_PARMSW* = ptr MCI_OVLY_SAVE_PARMSW PMCI_OVLY_SAVE_PARMSW* = ptr MCI_OVLY_SAVE_PARMSW
LPMCI_OVLY_SAVE_PARMSW* = ptr MCI_OVLY_SAVE_PARMSW LPMCI_OVLY_SAVE_PARMSW* = ptr MCI_OVLY_SAVE_PARMSW
@ -2216,7 +2214,7 @@ type
MCI_OVLY_LOAD_PARMSA* {.final.} = object MCI_OVLY_LOAD_PARMSA* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
lpfilename*: LPCSTR lpfilename*: LPCSTR
rc*: TRECT rc*: RECT
PMCI_OVLY_LOAD_PARMSA* = ptr MCI_OVLY_LOAD_PARMSA PMCI_OVLY_LOAD_PARMSA* = ptr MCI_OVLY_LOAD_PARMSA
LPMCI_OVLY_LOAD_PARMSA* = ptr MCI_OVLY_LOAD_PARMSA LPMCI_OVLY_LOAD_PARMSA* = ptr MCI_OVLY_LOAD_PARMSA
@ -2224,7 +2222,7 @@ type
MCI_OVLY_LOAD_PARMSW* {.final.} = object MCI_OVLY_LOAD_PARMSW* {.final.} = object
dwCallback*: DWORD dwCallback*: DWORD
lpfilename*: LPCWSTR lpfilename*: LPCWSTR
rc*: TRECT rc*: RECT
PMCI_OVLY_LOAD_PARMSW* = ptr MCI_OVLY_LOAD_PARMSW PMCI_OVLY_LOAD_PARMSW* = ptr MCI_OVLY_LOAD_PARMSW
LPMCI_OVLY_LOAD_PARMSW* = ptr MCI_OVLY_LOAD_PARMSW LPMCI_OVLY_LOAD_PARMSW* = ptr MCI_OVLY_LOAD_PARMSW

View file

@ -20,9 +20,9 @@ const
MAX_LANA* = 254 # lana's in range 0 to MAX_LANA inclusive MAX_LANA* = 254 # lana's in range 0 to MAX_LANA inclusive
type # Network Control Block type # Network Control Block
PNCB* = ptr TNCB PNCB* = ptr NCB
TNCBPostProc* = proc (P: PNCB) {.stdcall.} NCBPostProc* = proc (P: PNCB) {.stdcall.}
TNCB* {.final.} = object # Structure returned to the NCB command NCBASTAT is ADAPTER_STATUS followed NCB* {.final.} = object # Structure returned to the NCB command NCBASTAT is ADAPTER_STATUS followed
# by an array of NAME_BUFFER structures. # by an array of NAME_BUFFER structures.
ncb_command*: char # command code ncb_command*: char # command code
ncb_retcode*: char # return code ncb_retcode*: char # return code
@ -34,17 +34,17 @@ type # Network Control Block
ncb_name*: array[0..NCBNAMSZ - 1, char] # our blank-padded netname ncb_name*: array[0..NCBNAMSZ - 1, char] # our blank-padded netname
ncb_rto*: char # rcv timeout/retry count ncb_rto*: char # rcv timeout/retry count
ncb_sto*: char # send timeout/sys timeout ncb_sto*: char # send timeout/sys timeout
ncb_post*: TNCBPostProc # POST routine address ncb_post*: NCBPostProc # POST routine address
ncb_lana_num*: char # lana (adapter) number ncb_lana_num*: char # lana (adapter) number
ncb_cmd_cplt*: char # 0xff => commmand pending ncb_cmd_cplt*: char # 0xff => commmand pending
ncb_reserve*: array[0..9, char] # reserved, used by BIOS ncb_reserve*: array[0..9, char] # reserved, used by BIOS
ncb_event*: THandle # HANDLE to Win32 event which ncb_event*: Handle # HANDLE to Win32 event which
# will be set to the signalled # will be set to the signalled
# state when an ASYNCH command # state when an ASYNCH command
# completes # completes
PAdapterStatus* = ptr TAdapterStatus PAdapterStatus* = ptr AdapterStatus
TAdapterStatus* {.final.} = object AdapterStatus* {.final.} = object
adapter_address*: array[0..5, char] adapter_address*: array[0..5, char]
rev_major*: char rev_major*: char
reserved0*: char reserved0*: char
@ -73,11 +73,13 @@ type # Network Control Block
max_sess_pkt_size*: int16 max_sess_pkt_size*: int16
name_count*: int16 name_count*: int16
PNameBuffer* = ptr TNameBuffer PNameBuffer* = ptr NameBuffer
TNameBuffer* {.final.} = object NameBuffer* {.final.} = object
name*: array[0..NCBNAMSZ - 1, char] name*: array[0..NCBNAMSZ - 1, char]
name_num*: char name_num*: char
name_flags*: char name_flags*: char
{.deprecated: [TNCB: NCB, TNCBPostProc: NCBPostProc,
TAdapterStatus: AdapterStatus, TNameBuffer: NameBuffer].}
const # values for name_flags bits. const # values for name_flags bits.
@ -94,21 +96,22 @@ type # Structure returned to the NCB command NCBSSTAT is SESSION_HEADER followed
# by an array of SESSION_BUFFER structures. If the NCB_NAME starts with an # by an array of SESSION_BUFFER structures. If the NCB_NAME starts with an
# asterisk then an array of these structures is returned containing the # asterisk then an array of these structures is returned containing the
# status for all names. # status for all names.
PSessionHeader* = ptr TSessionHeader PSessionHeader* = ptr SessionHeader
TSessionHeader* {.final.} = object SessionHeader* {.final.} = object
sess_name*: char sess_name*: char
num_sess*: char num_sess*: char
rcv_dg_outstanding*: char rcv_dg_outstanding*: char
rcv_any_outstanding*: char rcv_any_outstanding*: char
PSessionBuffer* = ptr TSessionBuffer PSessionBuffer* = ptr SessionBuffer
TSessionBuffer* {.final.} = object SessionBuffer* {.final.} = object
lsn*: char lsn*: char
state*: char state*: char
local_name*: array[0..NCBNAMSZ - 1, char] local_name*: array[0..NCBNAMSZ - 1, char]
remote_name*: array[0..NCBNAMSZ - 1, char] remote_name*: array[0..NCBNAMSZ - 1, char]
rcvs_outstanding*: char rcvs_outstanding*: char
sends_outstanding*: char sends_outstanding*: char
{.deprecated: [TSessionHeader: SessionHeader, TSessionBuffer: SessionBuffer].}
const # Values for state const # Values for state
@ -128,14 +131,14 @@ type # Structure returned to the NCB command NCBENUM.
len*: char # Number of valid entries in lana[] len*: char # Number of valid entries in lana[]
lana*: array[0..MAX_LANA, char] lana*: array[0..MAX_LANA, char]
PFindNameHeader* = ptr TFindNameHeader PFindNameHeader* = ptr FindNameHeader
TFindNameHeader* {.final.} = object FindNameHeader* {.final.} = object
node_count*: int16 node_count*: int16
reserved*: char reserved*: char
unique_group*: char unique_group*: char
PFindNameBuffer* = ptr TFindNameBuffer PFindNameBuffer* = ptr FindNameBuffer
TFindNameBuffer* {.final.} = object # Structure provided with NCBACTION. The purpose of NCBACTION is to provide FindNameBuffer* {.final.} = object # Structure provided with NCBACTION. The purpose of NCBACTION is to provide
# transport specific extensions to netbios. # transport specific extensions to netbios.
len*: char len*: char
access_control*: char access_control*: char
@ -144,12 +147,13 @@ type # Structure returned to the NCB command NCBENUM.
source_addr*: array[0..5, char] source_addr*: array[0..5, char]
routing_info*: array[0..17, char] routing_info*: array[0..17, char]
PActionHeader* = ptr TActionHeader PActionHeader* = ptr ActionHeader
TActionHeader* {.final.} = object ActionHeader* {.final.} = object
transport_id*: int32 transport_id*: int32
action_code*: int16 action_code*: int16
reserved*: int16 reserved*: int16
{.deprecated: [TFindNameHeader: FindNameHeader, TFindNameBuffer: FindNameBuffer,
TActionHeader: ActionHeader].}
const # Values for transport_id const # Values for transport_id
ALL_TRANSPORTS* = "M\0\0\0" ALL_TRANSPORTS* = "M\0\0\0"

View file

@ -31,7 +31,7 @@ import
windows windows
type type
HDROP* = THandle HDROP* = Handle
UINT_PTR* = ptr uint32 UINT_PTR* = ptr uint32
DWORD_PTR* = ptr DWORD DWORD_PTR* = ptr DWORD
PHICON* = ptr HICON PHICON* = ptr HICON
@ -57,8 +57,8 @@ type
hStdError*: HANDLE hStdError*: HANDLE
LPSTARTUPINFOW* = ptr STARTUPINFOW LPSTARTUPINFOW* = ptr STARTUPINFOW
TSTARTUPINFOW* = STARTUPINFOW
PSTARTUPINFOW* = ptr STARTUPINFOW #unicode PSTARTUPINFOW* = ptr STARTUPINFOW #unicode
{.deprecated: [TSTARTUPINFOW: STARTUPINFOW].}
proc DragQueryFileA*(arg1: HDROP, arg2: uint32, arg3: LPSTR, arg4: uint32): uint32{. proc DragQueryFileA*(arg1: HDROP, arg2: uint32, arg3: LPSTR, arg4: uint32): uint32{.
stdcall, dynlib: "shell32.dll", importc: "DragQueryFileA".} stdcall, dynlib: "shell32.dll", importc: "DragQueryFileA".}
@ -132,7 +132,6 @@ type # init with sizeof(DRAGINFO)
lpFileList*: LPSTR lpFileList*: LPSTR
grfKeyState*: DWORD grfKeyState*: DWORD
TDRAGINFOA* = DRAGINFOA
LPDRAGINFOA* = ptr DRAGINFOA # init with sizeof(DRAGINFO) LPDRAGINFOA* = ptr DRAGINFOA # init with sizeof(DRAGINFO)
DRAGINFOW* {.final.} = object DRAGINFOW* {.final.} = object
uSize*: uint32 uSize*: uint32
@ -141,19 +140,19 @@ type # init with sizeof(DRAGINFO)
lpFileList*: LPWSTR lpFileList*: LPWSTR
grfKeyState*: DWORD grfKeyState*: DWORD
TDRAGINFOW* = DRAGINFOW
LPDRAGINFOW* = ptr DRAGINFOW LPDRAGINFOW* = ptr DRAGINFOW
{.deprecated: [TDRAGINFOA: DRAGINFOA, TDRAGINFOW: DRAGINFOW].}
when defined(UNICODE): when defined(UNICODE):
type type
DRAGINFO* = DRAGINFOW DRAGINFO* = DRAGINFOW
TDRAGINFO* = DRAGINFOW
LPDRAGINFO* = LPDRAGINFOW LPDRAGINFO* = LPDRAGINFOW
{.deprecated: [TDRAGINFO: DRAGINFOW].}
else: else:
type type
DRAGINFO* = DRAGINFOA DRAGINFO* = DRAGINFOA
TDRAGINFO* = DRAGINFOW
LPDRAGINFO* = LPDRAGINFOA LPDRAGINFO* = LPDRAGINFOA
{.deprecated: [TDRAGINFO: DRAGINFOW].}
const const
ABM_NEW* = 0x00000000 ABM_NEW* = 0x00000000
ABM_REMOVE* = 0x00000001 ABM_REMOVE* = 0x00000001
@ -189,8 +188,8 @@ type
rc*: RECT rc*: RECT
lParam*: LPARAM # message specific lParam*: LPARAM # message specific
TAPPBARDATA* = AppBarData
PAPPBARDATA* = ptr AppBarData PAPPBARDATA* = ptr AppBarData
{.deprecated: [TAPPBARDATA: AppBarData].}
proc SHAppBarMessage*(dwMessage: DWORD, pData: APPBARDATA): UINT_PTR{.stdcall, proc SHAppBarMessage*(dwMessage: DWORD, pData: APPBARDATA): UINT_PTR{.stdcall,
dynlib: "shell32.dll", importc: "SHAppBarMessage".} dynlib: "shell32.dll", importc: "SHAppBarMessage".}
@ -299,7 +298,6 @@ type
hNameMappings*: LPVOID hNameMappings*: LPVOID
lpszProgressTitle*: LPCSTR # only used if FOF_SIMPLEPROGRESS lpszProgressTitle*: LPCSTR # only used if FOF_SIMPLEPROGRESS
TSHFILEOPSTRUCTA* = SHFILEOPSTRUCTA
LPSHFILEOPSTRUCTA* = ptr SHFILEOPSTRUCTA LPSHFILEOPSTRUCTA* = ptr SHFILEOPSTRUCTA
SHFILEOPSTRUCTW* {.final.} = object SHFILEOPSTRUCTW* {.final.} = object
hwnd*: HWND hwnd*: HWND
@ -311,19 +309,21 @@ type
hNameMappings*: LPVOID hNameMappings*: LPVOID
lpszProgressTitle*: LPCWSTR lpszProgressTitle*: LPCWSTR
TSHFILEOPSTRUCTW* = SHFILEOPSTRUCTW
LPSHFILEOPSTRUCTW* = ptr SHFILEOPSTRUCTW LPSHFILEOPSTRUCTW* = ptr SHFILEOPSTRUCTW
{.deprecated: [TSHFILEOPSTRUCTA: SHFILEOPSTRUCTA,
TSHFILEOPSTRUCTW: SHFILEOPSTRUCTW].}
when defined(UNICODE): when defined(UNICODE):
type type
SHFILEOPSTRUCT* = SHFILEOPSTRUCTW SHFILEOPSTRUCT* = SHFILEOPSTRUCTW
TSHFILEOPSTRUCT* = SHFILEOPSTRUCTW
LPSHFILEOPSTRUCT* = LPSHFILEOPSTRUCTW LPSHFILEOPSTRUCT* = LPSHFILEOPSTRUCTW
{.deprecated: [TSHFILEOPSTRUCT: SHFILEOPSTRUCTW].}
else: else:
type type
SHFILEOPSTRUCT* = SHFILEOPSTRUCTA SHFILEOPSTRUCT* = SHFILEOPSTRUCTA
TSHFILEOPSTRUCT* = SHFILEOPSTRUCTA
LPSHFILEOPSTRUCT* = LPSHFILEOPSTRUCTA LPSHFILEOPSTRUCT* = LPSHFILEOPSTRUCTA
{.deprecated: [TSHFILEOPSTRUCT: SHFILEOPSTRUCTA].}
proc SHFileOperationA*(lpFileOp: LPSHFILEOPSTRUCTA): int32{.stdcall, proc SHFileOperationA*(lpFileOp: LPSHFILEOPSTRUCTA): int32{.stdcall,
dynlib: "shell32.dll", importc: "SHFileOperationA".} dynlib: "shell32.dll", importc: "SHFileOperationA".}
proc SHFileOperationW*(lpFileOp: LPSHFILEOPSTRUCTW): int32{.stdcall, proc SHFileOperationW*(lpFileOp: LPSHFILEOPSTRUCTW): int32{.stdcall,
@ -332,7 +332,7 @@ proc SHFileOperation*(lpFileOp: LPSHFILEOPSTRUCTA): int32{.stdcall,
dynlib: "shell32.dll", importc: "SHFileOperationA".} dynlib: "shell32.dll", importc: "SHFileOperationA".}
proc SHFileOperation*(lpFileOp: LPSHFILEOPSTRUCTW): int32{.stdcall, proc SHFileOperation*(lpFileOp: LPSHFILEOPSTRUCTW): int32{.stdcall,
dynlib: "shell32.dll", importc: "SHFileOperationW".} dynlib: "shell32.dll", importc: "SHFileOperationW".}
proc SHFreeNameMappings*(hNameMappings: THandle){.stdcall, proc SHFreeNameMappings*(hNameMappings: Handle){.stdcall,
dynlib: "shell32.dll", importc: "SHFreeNameMappings".} dynlib: "shell32.dll", importc: "SHFreeNameMappings".}
type type
SHNAMEMAPPINGA* {.final.} = object SHNAMEMAPPINGA* {.final.} = object
@ -341,7 +341,6 @@ type
cchOldPath*: int32 cchOldPath*: int32
cchNewPath*: int32 cchNewPath*: int32
TSHNAMEMAPPINGA* = SHNAMEMAPPINGA
LPSHNAMEMAPPINGA* = ptr SHNAMEMAPPINGA LPSHNAMEMAPPINGA* = ptr SHNAMEMAPPINGA
SHNAMEMAPPINGW* {.final.} = object SHNAMEMAPPINGW* {.final.} = object
pszOldPath*: LPWSTR pszOldPath*: LPWSTR
@ -349,19 +348,20 @@ type
cchOldPath*: int32 cchOldPath*: int32
cchNewPath*: int32 cchNewPath*: int32
TSHNAMEMAPPINGW* = SHNAMEMAPPINGW
LPSHNAMEMAPPINGW* = ptr SHNAMEMAPPINGW LPSHNAMEMAPPINGW* = ptr SHNAMEMAPPINGW
{.deprecated: [TSHNAMEMAPPINGA: SHNAMEMAPPINGA,
TSHNAMEMAPPINGW: SHNAMEMAPPINGW].}
when not(defined(UNICODE)): when not(defined(UNICODE)):
type type
SHNAMEMAPPING* = SHNAMEMAPPINGW SHNAMEMAPPING* = SHNAMEMAPPINGW
TSHNAMEMAPPING* = SHNAMEMAPPINGW
LPSHNAMEMAPPING* = LPSHNAMEMAPPINGW LPSHNAMEMAPPING* = LPSHNAMEMAPPINGW
{.deprecated: [TSHNAMEMAPPING: SHNAMEMAPPINGW].}
else: else:
type type
SHNAMEMAPPING* = SHNAMEMAPPINGA SHNAMEMAPPING* = SHNAMEMAPPINGA
TSHNAMEMAPPING* = SHNAMEMAPPINGA
LPSHNAMEMAPPING* = LPSHNAMEMAPPINGA LPSHNAMEMAPPING* = LPSHNAMEMAPPINGA
{.deprecated: [TSHNAMEMAPPING: SHNAMEMAPPINGA].}
# #
# End Shell File Operations # End Shell File Operations
# #
@ -424,7 +424,6 @@ type
hMonitor*: HANDLE # also: hIcon hMonitor*: HANDLE # also: hIcon
hProcess*: HANDLE hProcess*: HANDLE
TSHELLEXECUTEINFOA* = SHELLEXECUTEINFOA
LPSHELLEXECUTEINFOA* = ptr SHELLEXECUTEINFOA LPSHELLEXECUTEINFOA* = ptr SHELLEXECUTEINFOA
SHELLEXECUTEINFOW* {.final.} = object SHELLEXECUTEINFOW* {.final.} = object
cbSize*: DWORD cbSize*: DWORD
@ -443,19 +442,21 @@ type
hMonitor*: HANDLE # also: hIcon hMonitor*: HANDLE # also: hIcon
hProcess*: HANDLE hProcess*: HANDLE
TSHELLEXECUTEINFOW* = SHELLEXECUTEINFOW
LPSHELLEXECUTEINFOW* = ptr SHELLEXECUTEINFOW LPSHELLEXECUTEINFOW* = ptr SHELLEXECUTEINFOW
{.deprecated: [TSHELLEXECUTEINFOA: SHELLEXECUTEINFOA,
TSHELLEXECUTEINFOW: SHELLEXECUTEINFOW].}
when defined(UNICODE): when defined(UNICODE):
type type
SHELLEXECUTEINFO* = SHELLEXECUTEINFOW SHELLEXECUTEINFO* = SHELLEXECUTEINFOW
TSHELLEXECUTEINFO* = SHELLEXECUTEINFOW
LPSHELLEXECUTEINFO* = LPSHELLEXECUTEINFOW LPSHELLEXECUTEINFO* = LPSHELLEXECUTEINFOW
{.deprecated: [TSHELLEXECUTEINFO: SHELLEXECUTEINFOW].}
else: else:
type type
SHELLEXECUTEINFO* = SHELLEXECUTEINFOA SHELLEXECUTEINFO* = SHELLEXECUTEINFOA
TSHELLEXECUTEINFO* = SHELLEXECUTEINFOA
LPSHELLEXECUTEINFO* = LPSHELLEXECUTEINFOA LPSHELLEXECUTEINFO* = LPSHELLEXECUTEINFOA
{.deprecated: [TSHELLEXECUTEINFO: SHELLEXECUTEINFOA].}
proc ShellExecuteExA*(lpExecInfo: LPSHELLEXECUTEINFOA): bool{.stdcall, proc ShellExecuteExA*(lpExecInfo: LPSHELLEXECUTEINFOA): bool{.stdcall,
dynlib: "shell32.dll", importc: "ShellExecuteExA".} dynlib: "shell32.dll", importc: "ShellExecuteExA".}
proc ShellExecuteExW*(lpExecInfo: LPSHELLEXECUTEINFOW): bool{.stdcall, proc ShellExecuteExW*(lpExecInfo: LPSHELLEXECUTEINFOW): bool{.stdcall,
@ -492,8 +493,8 @@ type
lpStartupInfo*: LPSTARTUPINFOW lpStartupInfo*: LPSTARTUPINFOW
lpProcessInformation*: LPPROCESS_INFORMATION lpProcessInformation*: LPPROCESS_INFORMATION
TSHCREATEPROCESSINFOW* = SHCREATEPROCESSINFOW
PSHCREATEPROCESSINFOW* = ptr SHCREATEPROCESSINFOW PSHCREATEPROCESSINFOW* = ptr SHCREATEPROCESSINFOW
{.deprecated: [TSHCREATEPROCESSINFOW: SHCREATEPROCESSINFOW].}
proc SHCreateProcessAsUserW*(pscpi: PSHCREATEPROCESSINFOW): bool{.stdcall, proc SHCreateProcessAsUserW*(pscpi: PSHCREATEPROCESSINFOW): bool{.stdcall,
dynlib: "shell32.dll", importc: "SHCreateProcessAsUserW".} dynlib: "shell32.dll", importc: "SHCreateProcessAsUserW".}
@ -510,8 +511,8 @@ type
i64Size*: int64 i64Size*: int64
i64NumItems*: int64 i64NumItems*: int64
TSHQUERYRBINFO* = SHQUERYRBINFO
LPSHQUERYRBINFO* = ptr SHQUERYRBINFO # flags for SHEmptyRecycleBin LPSHQUERYRBINFO* = ptr SHQUERYRBINFO # flags for SHEmptyRecycleBin
{.deprecated: [TSHQUERYRBINFO: SHQUERYRBINFO].}
const const
SHERB_NOCONFIRMATION* = 0x00000001 SHERB_NOCONFIRMATION* = 0x00000001
@ -555,9 +556,8 @@ type
uTimeout*: uint32 # also: uVersion uTimeout*: uint32 # also: uVersion
szInfoTitle*: array[0..63, char] szInfoTitle*: array[0..63, char]
dwInfoFlags*: DWORD dwInfoFlags*: DWORD
guidItem*: TGUID guidItem*: GUID
TNOTIFYICONDATAA* = NOTIFYICONDATAA
PNOTIFYICONDATAA* = ptr NOTIFYICONDATAA PNOTIFYICONDATAA* = ptr NOTIFYICONDATAA
NOTIFYICONDATAW* {.final.} = object NOTIFYICONDATAW* {.final.} = object
cbSize*: DWORD cbSize*: DWORD
@ -573,21 +573,22 @@ type
uTimeout*: uint32 # also uVersion : UINT uTimeout*: uint32 # also uVersion : UINT
szInfoTitle*: array[0..63, char] szInfoTitle*: array[0..63, char]
dwInfoFlags*: DWORD dwInfoFlags*: DWORD
guidItem*: TGUID guidItem*: GUID
TNOTIFYICONDATAW* = NOTIFYICONDATAW
PNOTIFYICONDATAW* = ptr NOTIFYICONDATAW PNOTIFYICONDATAW* = ptr NOTIFYICONDATAW
{.deprecated: [TNOTIFYICONDATAA: NOTIFYICONDATAA,
TNOTIFYICONDATAW: NOTIFYICONDATAW].}
when defined(UNICODE): when defined(UNICODE):
type type
NOTIFYICONDATA* = NOTIFYICONDATAW NOTIFYICONDATA* = NOTIFYICONDATAW
TNOTIFYICONDATA* = NOTIFYICONDATAW
PNOTIFYICONDATA* = PNOTIFYICONDATAW PNOTIFYICONDATA* = PNOTIFYICONDATAW
{.deprecated: [TNOTIFYICONDATA: NOTIFYICONDATAW].}
else: else:
type type
NOTIFYICONDATA* = NOTIFYICONDATAA NOTIFYICONDATA* = NOTIFYICONDATAA
TNOTIFYICONDATA* = NOTIFYICONDATAA
PNOTIFYICONDATA* = PNOTIFYICONDATAA PNOTIFYICONDATA* = PNOTIFYICONDATAA
{.deprecated: [TNOTIFYICONDATA: NOTIFYICONDATAA].}
const const
NIN_SELECT* = WM_USER + 0 NIN_SELECT* = WM_USER + 0
NINF_KEY* = 0x00000001 NINF_KEY* = 0x00000001
@ -655,7 +656,6 @@ type
szDisplayName*: array[0..(MAX_PATH) - 1, char] # out: display name (or path) szDisplayName*: array[0..(MAX_PATH) - 1, char] # out: display name (or path)
szTypeName*: array[0..79, char] # out: type name szTypeName*: array[0..79, char] # out: type name
TSHFILEINFOA* = SHFILEINFOA
PSHFILEINFOA* = ptr SHFILEINFOA PSHFILEINFOA* = ptr SHFILEINFOA
SHFILEINFOW* {.final.} = object SHFILEINFOW* {.final.} = object
hIcon*: HICON # out: icon hIcon*: HICON # out: icon
@ -664,19 +664,19 @@ type
szDisplayName*: array[0..(MAX_PATH) - 1, Wchar] # out: display name (or path) szDisplayName*: array[0..(MAX_PATH) - 1, Wchar] # out: display name (or path)
szTypeName*: array[0..79, Wchar] # out: type name szTypeName*: array[0..79, Wchar] # out: type name
TSHFILEINFOW* = SHFILEINFOW
PSHFILEINFOW* = ptr SHFILEINFOW PSHFILEINFOW* = ptr SHFILEINFOW
{.deprecated: [TSHFILEINFOA: SHFILEINFOA, TSHFILEINFOW: SHFILEINFOW].}
when defined(UNICODE): when defined(UNICODE):
type type
SHFILEINFO* = SHFILEINFOW SHFILEINFO* = SHFILEINFOW
TSHFILEINFO* = SHFILEINFOW
pFILEINFO* = SHFILEINFOW pFILEINFO* = SHFILEINFOW
{.deprecated: [TSHFILEINFO: SHFILEINFOW].}
else: else:
type type
SHFILEINFO* = SHFILEINFOA SHFILEINFO* = SHFILEINFOA
TSHFILEINFO* = SHFILEINFOA
pFILEINFO* = SHFILEINFOA pFILEINFO* = SHFILEINFOA
{.deprecated: [TSHFILEINFO: SHFILEINFOA].}
# NOTE: This is also in shlwapi.h. Please keep in synch. # NOTE: This is also in shlwapi.h. Please keep in synch.
const const
@ -710,16 +710,16 @@ proc SHGetFileInfo*(pszPath: LPCSTR, dwFileAttributes: DWORD,
psfi: PSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. psfi: PSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{.
stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".}
proc SHGetFileInfoA*(pszPath: LPCSTR, dwFileAttributes: DWORD, proc SHGetFileInfoA*(pszPath: LPCSTR, dwFileAttributes: DWORD,
psfi: var TSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. psfi: var SHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{.
stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".}
proc SHGetFileInfoW*(pszPath: LPCWSTR, dwFileAttributes: DWORD, proc SHGetFileInfoW*(pszPath: LPCWSTR, dwFileAttributes: DWORD,
psfi: var TSHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{. psfi: var SHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{.
stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".} stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".}
proc SHGetFileInfo*(pszPath: LPCSTR, dwFileAttributes: DWORD, proc SHGetFileInfo*(pszPath: LPCSTR, dwFileAttributes: DWORD,
psfi: var TSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. psfi: var SHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{.
stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".}
proc SHGetFileInfo*(pszPath: LPCWSTR, dwFileAttributes: DWORD, proc SHGetFileInfo*(pszPath: LPCWSTR, dwFileAttributes: DWORD,
psfi: var TSHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{. psfi: var SHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{.
stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".} stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".}
proc SHGetDiskFreeSpaceExA*(pszDirectoryName: LPCSTR, proc SHGetDiskFreeSpaceExA*(pszDirectoryName: LPCSTR,
pulFreeBytesAvailableToCaller: PULARGE_INTEGER, pulFreeBytesAvailableToCaller: PULARGE_INTEGER,

View file

@ -72,22 +72,22 @@ const
CSIDL_FLAG_CREATE* = 0x00008000 # (force creation of requested folder if it doesn't exist yet) CSIDL_FLAG_CREATE* = 0x00008000 # (force creation of requested folder if it doesn't exist yet)
# Original entry points # Original entry points
proc SHGetFolderPathA*(Ahwnd: HWND, Csidl: int, Token: THandle, Flags: DWord, proc SHGetFolderPathA*(Ahwnd: HWND, Csidl: int, Token: Handle, Flags: DWord,
Path: cstring): HRESULT{.stdcall, dynlib: LibName, Path: cstring): HRESULT{.stdcall, dynlib: LibName,
importc: "SHGetFolderPathA".} importc: "SHGetFolderPathA".}
proc SHGetFolderPathW*(Ahwnd: HWND, Csidl: int, Token: THandle, Flags: DWord, proc SHGetFolderPathW*(Ahwnd: HWND, Csidl: int, Token: Handle, Flags: DWord,
Path: cstring): HRESULT{.stdcall, dynlib: LibName, Path: cstring): HRESULT{.stdcall, dynlib: LibName,
importc: "SHGetFolderPathW".} importc: "SHGetFolderPathW".}
proc SHGetFolderPath*(Ahwnd: HWND, Csidl: int, Token: THandle, Flags: DWord, proc SHGetFolderPath*(Ahwnd: HWND, Csidl: int, Token: Handle, Flags: DWord,
Path: cstring): HRESULT{.stdcall, dynlib: LibName, Path: cstring): HRESULT{.stdcall, dynlib: LibName,
importc: "SHGetFolderPathA".} importc: "SHGetFolderPathA".}
type type
PFNSHGetFolderPathA* = proc (Ahwnd: HWND, Csidl: int, Token: THandle, PFNSHGetFolderPathA* = proc (Ahwnd: HWND, Csidl: int, Token: Handle,
Flags: DWord, Path: cstring): HRESULT{.stdcall.} Flags: DWord, Path: cstring): HRESULT{.stdcall.}
PFNSHGetFolderPathW* = proc (Ahwnd: HWND, Csidl: int, Token: THandle, PFNSHGetFolderPathW* = proc (Ahwnd: HWND, Csidl: int, Token: Handle,
Flags: DWord, Path: cstring): HRESULT{.stdcall.} Flags: DWord, Path: cstring): HRESULT{.stdcall.}
PFNSHGetFolderPath* = PFNSHGetFolderPathA PFNSHGetFolderPath* = PFNSHGetFolderPathA
TSHGetFolderPathA* = PFNSHGetFolderPathA
TSHGetFolderPathW* = PFNSHGetFolderPathW {.deprecated: [TSHGetFolderPathA: PFNSHGetFolderPathA,
TSHGetFolderPath* = TSHGetFolderPathA TSHGetFolderPathW: PFNSHGetFolderPathW,
TSHGetFolderPath: SHGetFolderPathA].}

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ const
useWinUnicode* = not defined(useWinAnsi) useWinUnicode* = not defined(useWinAnsi)
type type
THandle* = int Handle* = int
LONG* = int32 LONG* = int32
ULONG* = int ULONG* = int
PULONG* = ptr int PULONG* = ptr int
@ -24,15 +24,15 @@ type
DWORD* = int32 DWORD* = int32
PDWORD* = ptr DWORD PDWORD* = ptr DWORD
LPINT* = ptr int32 LPINT* = ptr int32
HDC* = THandle HDC* = Handle
HGLRC* = THandle HGLRC* = Handle
TSECURITY_ATTRIBUTES* {.final, pure.} = object SECURITY_ATTRIBUTES* {.final, pure.} = object
nLength*: int32 nLength*: int32
lpSecurityDescriptor*: pointer lpSecurityDescriptor*: pointer
bInheritHandle*: WINBOOL bInheritHandle*: WINBOOL
TSTARTUPINFO* {.final, pure.} = object STARTUPINFO* {.final, pure.} = object
cb*: int32 cb*: int32
lpReserved*: cstring lpReserved*: cstring
lpDesktop*: cstring lpDesktop*: cstring
@ -48,25 +48,25 @@ type
wShowWindow*: int16 wShowWindow*: int16
cbReserved2*: int16 cbReserved2*: int16
lpReserved2*: pointer lpReserved2*: pointer
hStdInput*: THandle hStdInput*: Handle
hStdOutput*: THandle hStdOutput*: Handle
hStdError*: THandle hStdError*: Handle
TPROCESS_INFORMATION* {.final, pure.} = object PROCESS_INFORMATION* {.final, pure.} = object
hProcess*: THandle hProcess*: Handle
hThread*: THandle hThread*: Handle
dwProcessId*: int32 dwProcessId*: int32
dwThreadId*: int32 dwThreadId*: int32
TFILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
dwLowDateTime*: DWORD dwLowDateTime*: DWORD
dwHighDateTime*: DWORD dwHighDateTime*: DWORD
TBY_HANDLE_FILE_INFORMATION* {.final, pure.} = object BY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
dwFileAttributes*: DWORD dwFileAttributes*: DWORD
ftCreationTime*: TFILETIME ftCreationTime*: FILETIME
ftLastAccessTime*: TFILETIME ftLastAccessTime*: FILETIME
ftLastWriteTime*: TFILETIME ftLastWriteTime*: FILETIME
dwVolumeSerialNumber*: DWORD dwVolumeSerialNumber*: DWORD
nFileSizeHigh*: DWORD nFileSizeHigh*: DWORD
nFileSizeLow*: DWORD nFileSizeLow*: DWORD
@ -74,10 +74,16 @@ type
nFileIndexHigh*: DWORD nFileIndexHigh*: DWORD
nFileIndexLow*: DWORD nFileIndexLow*: DWORD
{.deprecated: [THandle: Handle, TSECURITY_ATTRIBUTES: SECURITY_ATTRIBUTES,
TSTARTUPINFO: STARTUPINFO, TPROCESS_INFORMATION: PROCESS_INFORMATION,
TFILETIME: FILETIME, TBY_HANDLE_FILE_INFORMATION: BY_HANDLE_FILE_INFORMATION].}
when useWinUnicode: when useWinUnicode:
type TWinChar* = TUtf16Char type WinChar* = Utf16Char
{.deprecated: [TWinChar: WinChar].}
else: else:
type TWinChar* = char type WinChar* = char
{.deprecated: [TWinChar: WinChar].}
const const
STARTF_USESHOWWINDOW* = 1'i32 STARTF_USESHOWWINDOW* = 1'i32
@ -98,67 +104,67 @@ const
DETACHED_PROCESS* = 8'i32 DETACHED_PROCESS* = 8'i32
SW_SHOWNORMAL* = 1'i32 SW_SHOWNORMAL* = 1'i32
INVALID_HANDLE_VALUE* = THandle(-1) INVALID_HANDLE_VALUE* = Handle(-1)
CREATE_UNICODE_ENVIRONMENT* = 1024'i32 CREATE_UNICODE_ENVIRONMENT* = 1024'i32
proc closeHandle*(hObject: THandle): WINBOOL {.stdcall, dynlib: "kernel32", proc closeHandle*(hObject: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "CloseHandle".} importc: "CloseHandle".}
proc readFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32, proc readFile*(hFile: Handle, Buffer: pointer, nNumberOfBytesToRead: int32,
lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{. lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadFile".} stdcall, dynlib: "kernel32", importc: "ReadFile".}
proc writeFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32, proc writeFile*(hFile: Handle, Buffer: pointer, nNumberOfBytesToWrite: int32,
lpNumberOfBytesWritten: ptr int32, lpNumberOfBytesWritten: ptr int32,
lpOverlapped: pointer): WINBOOL{. lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "WriteFile".} stdcall, dynlib: "kernel32", importc: "WriteFile".}
proc createPipe*(hReadPipe, hWritePipe: var THandle, proc createPipe*(hReadPipe, hWritePipe: var Handle,
lpPipeAttributes: var TSECURITY_ATTRIBUTES, lpPipeAttributes: var SECURITY_ATTRIBUTES,
nSize: int32): WINBOOL{. nSize: int32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreatePipe".} stdcall, dynlib: "kernel32", importc: "CreatePipe".}
when useWinUnicode: when useWinUnicode:
proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES, lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES, lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32, bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment, lpCurrentDirectory: WideCString, lpEnvironment, lpCurrentDirectory: WideCString,
lpStartupInfo: var TSTARTUPINFO, lpStartupInfo: var STARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{. lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessW".} stdcall, dynlib: "kernel32", importc: "CreateProcessW".}
else: else:
proc createProcessA*(lpApplicationName, lpCommandLine: cstring, proc createProcessA*(lpApplicationName, lpCommandLine: cstring,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES, lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES, lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32, bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment: pointer, lpCurrentDirectory: cstring, lpEnvironment: pointer, lpCurrentDirectory: cstring,
lpStartupInfo: var TSTARTUPINFO, lpStartupInfo: var STARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{. lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessA".} stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
proc suspendThread*(hThread: THandle): int32 {.stdcall, dynlib: "kernel32", proc suspendThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
importc: "SuspendThread".} importc: "SuspendThread".}
proc resumeThread*(hThread: THandle): int32 {.stdcall, dynlib: "kernel32", proc resumeThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
importc: "ResumeThread".} importc: "ResumeThread".}
proc waitForSingleObject*(hHandle: THandle, dwMilliseconds: int32): int32 {. proc waitForSingleObject*(hHandle: Handle, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
proc terminateProcess*(hProcess: THandle, uExitCode: int): WINBOOL {.stdcall, proc terminateProcess*(hProcess: Handle, uExitCode: int): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "TerminateProcess".} dynlib: "kernel32", importc: "TerminateProcess".}
proc getExitCodeProcess*(hProcess: THandle, lpExitCode: var int32): WINBOOL {. proc getExitCodeProcess*(hProcess: Handle, lpExitCode: var int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".} stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}
proc getStdHandle*(nStdHandle: int32): THandle {.stdcall, dynlib: "kernel32", proc getStdHandle*(nStdHandle: int32): Handle {.stdcall, dynlib: "kernel32",
importc: "GetStdHandle".} importc: "GetStdHandle".}
proc setStdHandle*(nStdHandle: int32, hHandle: THandle): WINBOOL {.stdcall, proc setStdHandle*(nStdHandle: int32, hHandle: Handle): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "SetStdHandle".} dynlib: "kernel32", importc: "SetStdHandle".}
proc flushFileBuffers*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32", proc flushFileBuffers*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "FlushFileBuffers".} importc: "FlushFileBuffers".}
proc getLastError*(): int32 {.importc: "GetLastError", proc getLastError*(): int32 {.importc: "GetLastError",
@ -193,7 +199,7 @@ when useWinUnicode:
proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW".} stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW".}
proc getModuleFileNameW*(handle: THandle, buf: WideCString, proc getModuleFileNameW*(handle: Handle, buf: WideCString,
size: int32): int32 {.importc: "GetModuleFileNameW", size: int32): int32 {.importc: "GetModuleFileNameW",
dynlib: "kernel32", stdcall.} dynlib: "kernel32", stdcall.}
else: else:
@ -208,7 +214,7 @@ else:
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. proc getModuleFileNameA*(handle: Handle, buf: cstring, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode: when useWinUnicode:
@ -239,34 +245,35 @@ const
MAX_PATH* = 260 MAX_PATH* = 260
type type
TWIN32_FIND_DATA* {.pure.} = object WIN32_FIND_DATA* {.pure.} = object
dwFileAttributes*: int32 dwFileAttributes*: int32
ftCreationTime*: TFILETIME ftCreationTime*: FILETIME
ftLastAccessTime*: TFILETIME ftLastAccessTime*: FILETIME
ftLastWriteTime*: TFILETIME ftLastWriteTime*: FILETIME
nFileSizeHigh*: int32 nFileSizeHigh*: int32
nFileSizeLow*: int32 nFileSizeLow*: int32
dwReserved0: int32 dwReserved0: int32
dwReserved1: int32 dwReserved1: int32
cFileName*: array[0..(MAX_PATH) - 1, TWinChar] cFileName*: array[0..(MAX_PATH) - 1, WinChar]
cAlternateFileName*: array[0..13, TWinChar] cAlternateFileName*: array[0..13, WinChar]
{.deprecated: [TWIN32_FIND_DATA: WIN32_FIND_DATA].}
when useWinUnicode: when useWinUnicode:
proc findFirstFileW*(lpFileName: WideCString, proc findFirstFileW*(lpFileName: WideCString,
lpFindFileData: var TWIN32_FIND_DATA): THandle {. lpFindFileData: var WIN32_FIND_DATA): Handle {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileW".} stdcall, dynlib: "kernel32", importc: "FindFirstFileW".}
proc findNextFileW*(hFindFile: THandle, proc findNextFileW*(hFindFile: Handle,
lpFindFileData: var TWIN32_FIND_DATA): int32 {. lpFindFileData: var WIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileW".} stdcall, dynlib: "kernel32", importc: "FindNextFileW".}
else: else:
proc findFirstFileA*(lpFileName: cstring, proc findFirstFileA*(lpFileName: cstring,
lpFindFileData: var TWIN32_FIND_DATA): THANDLE {. lpFindFileData: var WIN32_FIND_DATA): THANDLE {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileA".} stdcall, dynlib: "kernel32", importc: "FindFirstFileA".}
proc findNextFileA*(hFindFile: THANDLE, proc findNextFileA*(hFindFile: THANDLE,
lpFindFileData: var TWIN32_FIND_DATA): int32 {. lpFindFileData: var WIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileA".} stdcall, dynlib: "kernel32", importc: "FindNextFileA".}
proc findClose*(hFindFile: THandle) {.stdcall, dynlib: "kernel32", proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32",
importc: "FindClose".} importc: "FindClose".}
when useWinUnicode: when useWinUnicode:
@ -326,32 +333,32 @@ else:
proc getCommandLineA*(): cstring {. proc getCommandLineA*(): cstring {.
importc: "GetCommandLineA", stdcall, dynlib: "kernel32".} importc: "GetCommandLineA", stdcall, dynlib: "kernel32".}
proc rdFileTime*(f: TFILETIME): int64 = proc rdFileTime*(f: FILETIME): int64 =
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32) result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
proc rdFileSize*(f: TWIN32_FIND_DATA): int64 = proc rdFileSize*(f: WIN32_FIND_DATA): int64 =
result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32) result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {. proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.} importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.}
proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32", proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
importc: "Sleep".} importc: "Sleep".}
when useWinUnicode: when useWinUnicode:
proc shellExecuteW*(HWND: THandle, lpOperation, lpFile, proc shellExecuteW*(HWND: Handle, lpOperation, lpFile,
lpParameters, lpDirectory: WideCString, lpParameters, lpDirectory: WideCString,
nShowCmd: int32): THandle{. nShowCmd: int32): Handle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW".} stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW".}
else: else:
proc shellExecuteA*(HWND: THandle, lpOperation, lpFile, proc shellExecuteA*(HWND: Handle, lpOperation, lpFile,
lpParameters, lpDirectory: cstring, lpParameters, lpDirectory: cstring,
nShowCmd: int32): THandle{. nShowCmd: int32): Handle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".} stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
proc getFileInformationByHandle*(hFile: THandle, proc getFileInformationByHandle*(hFile: Handle,
lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{. lpFileInformation: ptr BY_HANDLE_FILE_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".} stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
const const
@ -374,7 +381,6 @@ proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.}
type type
SocketHandle* = distinct int SocketHandle* = distinct int
{.deprecated: [TSocketHandle: SocketHandle].} {.deprecated: [TSocketHandle: SocketHandle].}
type type
@ -451,7 +457,6 @@ type
ai_next*: ptr AddrInfo ## Pointer to next in list. ai_next*: ptr AddrInfo ## Pointer to next in list.
SockLen* = cuint SockLen* = cuint
{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, {.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo,
TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval,
TWSADATA: WSADATA, Thostent: Hostent, TServent: Servent, TWSADATA: WSADATA, Thostent: Hostent, TServent: Servent,
@ -571,8 +576,9 @@ const
MAXIMUM_WAIT_OBJECTS* = 0x00000040 MAXIMUM_WAIT_OBJECTS* = 0x00000040
type type
TWOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, THandle] WOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, Handle]
PWOHandleArray* = ptr TWOHandleArray PWOHandleArray* = ptr WOHandleArray
{.deprecated: [TWOHandleArray: WOHandleArray].}
proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray, proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{. bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
@ -613,7 +619,7 @@ when useWinUnicode:
proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD, proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer, lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD, dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THandle): THandle {. hTemplateFile: Handle): Handle {.
stdcall, dynlib: "kernel32", importc: "CreateFileW".} stdcall, dynlib: "kernel32", importc: "CreateFileW".}
proc deleteFileW*(pathName: WideCString): int32 {. proc deleteFileW*(pathName: WideCString): int32 {.
importc: "DeleteFileW", dynlib: "kernel32", stdcall.} importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
@ -626,28 +632,28 @@ else:
proc deleteFileA*(pathName: cstring): int32 {. proc deleteFileA*(pathName: cstring): int32 {.
importc: "DeleteFileA", dynlib: "kernel32", stdcall.} importc: "DeleteFileA", dynlib: "kernel32", stdcall.}
proc setEndOfFile*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32", proc setEndOfFile*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "SetEndOfFile".} importc: "SetEndOfFile".}
proc setFilePointer*(hFile: THandle, lDistanceToMove: LONG, proc setFilePointer*(hFile: Handle, lDistanceToMove: LONG,
lpDistanceToMoveHigh: ptr LONG, lpDistanceToMoveHigh: ptr LONG,
dwMoveMethod: DWORD): DWORD {. dwMoveMethod: DWORD): DWORD {.
stdcall, dynlib: "kernel32", importc: "SetFilePointer".} stdcall, dynlib: "kernel32", importc: "SetFilePointer".}
proc getFileSize*(hFile: THandle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall, proc getFileSize*(hFile: Handle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall,
dynlib: "kernel32", importc: "GetFileSize".} dynlib: "kernel32", importc: "GetFileSize".}
proc mapViewOfFileEx*(hFileMappingObject: THandle, dwDesiredAccess: DWORD, proc mapViewOfFileEx*(hFileMappingObject: Handle, dwDesiredAccess: DWORD,
dwFileOffsetHigh, dwFileOffsetLow: DWORD, dwFileOffsetHigh, dwFileOffsetLow: DWORD,
dwNumberOfBytesToMap: DWORD, dwNumberOfBytesToMap: DWORD,
lpBaseAddress: pointer): pointer{. lpBaseAddress: pointer): pointer{.
stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".} stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}
proc createFileMappingW*(hFile: THandle, proc createFileMappingW*(hFile: Handle,
lpFileMappingAttributes: pointer, lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD, flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD, dwMaximumSizeLow: DWORD,
lpName: pointer): THandle {. lpName: pointer): Handle {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".} stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}
when not useWinUnicode: when not useWinUnicode:
@ -661,23 +667,24 @@ proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".} dynlib: "kernel32", importc: "UnmapViewOfFile".}
type type
TOVERLAPPED* {.pure, inheritable.} = object OVERLAPPED* {.pure, inheritable.} = object
internal*: PULONG internal*: PULONG
internalHigh*: PULONG internalHigh*: PULONG
offset*: DWORD offset*: DWORD
offsetHigh*: DWORD offsetHigh*: DWORD
hEvent*: THandle hEvent*: Handle
POVERLAPPED* = ptr TOVERLAPPED POVERLAPPED* = ptr OVERLAPPED
POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD, POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
para3: POVERLAPPED){.stdcall.} para3: POVERLAPPED){.stdcall.}
TGUID* {.final, pure.} = object GUID* {.final, pure.} = object
D1*: int32 D1*: int32
D2*: int16 D2*: int16
D3*: int16 D3*: int16
D4*: array [0..7, int8] D4*: array [0..7, int8]
{.deprecated: [TOVERLAPPED: OVERLAPPED, TGUID: GUID].}
const const
ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING
@ -689,18 +696,18 @@ const
WSAETIMEDOUT* = 10060 WSAETIMEDOUT* = 10060
ERROR_NETNAME_DELETED* = 64 ERROR_NETNAME_DELETED* = 64
proc createIoCompletionPort*(FileHandle: THandle, ExistingCompletionPort: THandle, proc createIoCompletionPort*(FileHandle: Handle, ExistingCompletionPort: Handle,
CompletionKey: DWORD, CompletionKey: DWORD,
NumberOfConcurrentThreads: DWORD): THandle{.stdcall, NumberOfConcurrentThreads: DWORD): Handle{.stdcall,
dynlib: "kernel32", importc: "CreateIoCompletionPort".} dynlib: "kernel32", importc: "CreateIoCompletionPort".}
proc getQueuedCompletionStatus*(CompletionPort: THandle, proc getQueuedCompletionStatus*(CompletionPort: Handle,
lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG, lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG,
lpOverlapped: ptr POVERLAPPED, lpOverlapped: ptr POVERLAPPED,
dwMilliseconds: DWORD): WINBOOL{.stdcall, dwMilliseconds: DWORD): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".} dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
proc getOverlappedResult*(hFile: THandle, lpOverlapped: TOVERLAPPED, proc getOverlappedResult*(hFile: Handle, lpOverlapped: OVERLAPPED,
lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{. lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".} stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".}
@ -717,11 +724,11 @@ const
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
var var
WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [ WSAID_CONNECTEX*: GUID = GUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8]) 0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8])
WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ WSAID_ACCEPTEX*: GUID = GUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ WSAID_GETACCEPTEXSOCKADDRS*: GUID = GUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
@ -745,13 +752,13 @@ proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}
proc get_osfhandle*(fd:FileHandle): THandle {. proc get_osfhandle*(fd:FileHandle): Handle {.
importc: "_get_osfhandle", header:"<io.h>".} importc: "_get_osfhandle", header:"<io.h>".}
proc getSystemTimes*(lpIdleTime, lpKernelTime, proc getSystemTimes*(lpIdleTime, lpKernelTime,
lpUserTime: var TFILETIME): WINBOOL {.stdcall, lpUserTime: var FILETIME): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "GetSystemTimes".} dynlib: "kernel32", importc: "GetSystemTimes".}
proc getProcessTimes*(hProcess: THandle; lpCreationTime, lpExitTime, proc getProcessTimes*(hProcess: Handle; lpCreationTime, lpExitTime,
lpKernelTime, lpUserTime: var TFILETIME): WINBOOL {.stdcall, lpKernelTime, lpUserTime: var FILETIME): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "GetProcessTimes".} dynlib: "kernel32", importc: "GetProcessTimes".}

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more