fixes merge conflicts

This commit is contained in:
Andreas Rumpf 2018-09-11 17:27:47 +02:00
commit f7d1902043
887 changed files with 48670 additions and 42944 deletions

View file

@ -8,28 +8,41 @@
#
type
AllocatorFlag* {.pure.} = enum ## flags describing the properties of the allocator
ThreadLocal ## the allocator is thread local only.
ZerosMem ## the allocator always zeros the memory on an allocation
Allocator* = ptr object {.inheritable.}
alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.}
dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
deallocAll*: proc (a: Allocator) {.nimcall.}
flags*: set[AllocatorFlag]
var
currentAllocator {.threadvar.}: Allocator
localAllocator {.threadvar.}: Allocator
sharedAllocator: Allocator
proc getCurrentAllocator*(): Allocator =
result = currentAllocator
proc getLocalAllocator*(): Allocator =
result = localAllocator
proc setCurrentAllocator*(a: Allocator) =
currentAllocator = a
proc setLocalAllocator*(a: Allocator) =
localAllocator = a
proc alloc*(size: int; alignment: int = 8): pointer =
let a = getCurrentAllocator()
result = a.alloc(a, size, alignment)
proc getSharedAllocator*(): Allocator =
result = sharedAllocator
proc dealloc*(p: pointer; size: int) =
let a = getCurrentAllocator()
a.dealloc(a, p, size)
proc setSharedAllocator*(a: Allocator) =
sharedAllocator = a
proc realloc*(p: pointer; oldSize, newSize: int): pointer =
let a = getCurrentAllocator()
result = a.realloc(a, p, oldSize, newSize)
when false:
proc alloc*(size: int; alignment: int = 8): pointer =
let a = getCurrentAllocator()
result = a.alloc(a, size, alignment)
proc dealloc*(p: pointer; size: int) =
let a = getCurrentAllocator()
a.dealloc(a, p, size)
proc realloc*(p: pointer; oldSize, newSize: int): pointer =
let a = getCurrentAllocator()
result = a.realloc(a, p, oldSize, newSize)

47
lib/core/macrocache.nim Normal file
View file

@ -0,0 +1,47 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module provides an API for macros that need to collect compile
## time information across module boundaries in global variables.
## Starting with version 0.19 of Nim this is not directly supported anymore
## as it breaks incremental compilations.
## Instead the API here needs to be used. See XXX (wikipedia page) for a
## theoretical foundation behind this.
type
CacheSeq* = distinct string
CacheTable* = distinct string
CacheCounter* = distinct string
proc value*(c: CacheCounter): int {.magic: "NccValue".}
proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".}
proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".}
proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".}
proc len*(s: CacheSeq): int {.magic: "NcsLen".}
proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".}
iterator items*(s: CacheSeq): NimNode =
for i in 0 ..< len(s): yield s[i]
proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".}
## 'key' has to be unique!
proc len*(t: CacheTable): int {.magic: "NctLen".}
proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".}
proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
iterator pairs*(t: CacheTable): (string, NimNode) =
var h = 0
while hasNext(t, h):
let (a, b, h2) = next(t, h)
yield (a, b)
h = h2

View file

@ -8,12 +8,16 @@
#
include "system/inclrtl"
include "system/helpers"
## This module contains the interface to the compiler's abstract syntax
## tree (`AST`:idx:). Macros operate on this tree.
## .. include:: ../../doc/astspec.txt
# If you look for the implementation of the magic symbol
# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
type
NimNodeKind* = enum
nnkNone, nnkEmpty, nnkIdent, nnkSym,
@ -76,7 +80,8 @@ type
nnkGotoState,
nnkState,
nnkBreakState,
nnkFuncDef
nnkFuncDef,
nnkTupleConstr
NimNodeKinds* = set[NimNodeKind]
NimTypeKind* = enum # some types are no longer used, see ast.nim
@ -118,13 +123,10 @@ type
## use ``ident"abc"``.
NimSymObj = object # hidden
NimSym* = ref NimSymObj
NimSym* {.deprecated.} = ref NimSymObj
## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
## *ident*.
{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds,
TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind,
TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].}
const
nnkLiterals* = {nnkCharLit..nnkNilLit}
@ -134,25 +136,23 @@ const
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s`
## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
## **Deprecated since version 0.18.0**: Use ``ident`` or ``newIdentNode`` instead.
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s`
## **Deprecated since version 0.18.1**; Use ``ident`` or ``newIdentNode`` instead.
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
## converts a Nim identifier to a string
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
## converts a Nim symbol to a string
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.}
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated.}
## compares two Nim identifiers
## **Deprecated since version 0.18.1**; Use ``==`` on ``NimNode`` instead.
proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
## compares two Nim nodes
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.}
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
## compares two Nim symbols
## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
## compares two Nim nodes' types. Return true if the types are the same,
@ -195,8 +195,53 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated.} =
## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated.}
## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
when defined(nimSymKind):
proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.}
proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.}
proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
## retrieve the implementation of `symbol`. `symbol` can be a
## routine or a const.
proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated.}
## converts a Nim identifier to a string
## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated.}
## converts a Nim symbol to a string
## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
else: # bootstrapping substitute
proc getImpl*(symbol: NimNode): NimNode =
symbol.symbol.getImpl
proc strValOld(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
proc strVal*(n: NimNode): string =
if n.kind == nnkIdent:
$n.ident
elif n.kind == nnkSym:
$n.symbol
else:
n.strValOld
when defined(nimHasSymOwnerInMacro):
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
## accepts node of kind nnkSym and returns its owner's symbol.
## result is also mnde of kind nnkSym if owner exists otherwise
## nnkNilLit is returned
proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## with 'getType' you can access the node's `type`:idx:. A Nim type is
@ -214,26 +259,65 @@ proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
## Returns the type kind of the node 'n' that should represent a type, that
## means the node should have been obtained via `getType`.
## means the node should have been obtained via ``getType``.
proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for a specific instance
proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
## Returns the `type`:idx: of a node in a form matching the way the
## type instance was declared in the code.
runnableExamples:
type
Vec[N: static[int], T] = object
arr: array[N, T]
Vec4[T] = Vec[4, T]
Vec4f = Vec4[float32]
var a: Vec4f
var b: Vec4[float32]
var c: Vec[4, float32]
macro dumpTypeInst(x: typed): untyped =
newLit(x.getTypeInst.repr)
doAssert(dumpTypeInst(a) == "Vec4f")
doAssert(dumpTypeInst(b) == "Vec4[float32]")
doAssert(dumpTypeInst(c) == "Vec[4, float32]")
proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for a specific instance
## Version of ``getTypeInst`` which takes a ``typedesc``.
proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for the implementation
proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
## Returns the `type`:idx: of a node in a form matching the implementation
## of the type. Any intermediate aliases are expanded to arrive at the final
## type implementation. You can instead use ``getImpl`` on a symbol if you
## want to find the intermediate aliases.
runnableExamples:
type
Vec[N: static[int], T] = object
arr: array[N, T]
Vec4[T] = Vec[4, T]
Vec4f = Vec4[float32]
var a: Vec4f
var b: Vec4[float32]
var c: Vec[4, float32]
macro dumpTypeImpl(x: typed): untyped =
newLit(x.getTypeImpl.repr)
let t = """
object
arr: array[0 .. 3, float32]
"""
doAssert(dumpTypeImpl(a) == t)
doAssert(dumpTypeImpl(b) == t)
doAssert(dumpTypeImpl(c) == t)
proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for the implementation
proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
## Version of ``getTypeImpl`` which takes a ``typedesc``.
proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.}
proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect.}
proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated.}
## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``genSym`` instead.
proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated.}
## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``ident(string)`` instead.
#proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".}
# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
# can easily do:
@ -255,18 +339,13 @@ proc newNimNode*(kind: NimNodeKind,
proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect.} =
## retrieve the implementation of a symbol `s`. `s` can be a routine or a
## const.
discard
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
## writes an error message at compile time
proc warning*(msg: string) {.magic: "NWarning", benign.}
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
## writes a warning message at compile time
proc hint*(msg: string) {.magic: "NHint", benign.}
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
## writes a hint message at compile time
proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
@ -294,11 +373,9 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
result = newNimNode(nnkIdent)
result.ident = i
proc newIdentNode*(i: string): NimNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = toNimIdent i
proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.}
## creates an identifier node from `i`. It is simply an alias for
## ``ident(string)``. Use that, it's shorter.
type
BindSymRule* = enum ## specifies how ``bindSym`` behaves
@ -312,24 +389,34 @@ type
{.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {.
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol.
## if `ident` is a NimNode, it must have nkIdent kind.
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## returned even if the symbol is not ambiguous.
##
## experimental feature:
## use {.experimental: "dynamicBindSym".} to activate it
## if called from template / regular code, `ident` and `rule` must be
## constant expression / literal value.
## if called from macros / compile time procs / static blocks,
## `ident` and `rule` can be VM computed value.
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
magic: "NGenSym", noSideEffect.}
## generates a fresh symbol that is guaranteed to be unique. The symbol
## needs to occur in a declaration context.
proc callsite*(): NimNode {.magic: "NCallSite", benign.}
proc callsite*(): NimNode {.magic: "NCallSite", benign,
deprecated: "use varargs[untyped] in the macro prototype instead".}
## returns the AST of the invocation expression that invoked this macro.
## **Deprecated since version 0.18.1**.
proc toStrLit*(n: NimNode): NimNode {.compileTime.} =
## converts the AST `n` to the concrete Nim code and wraps that
@ -342,7 +429,8 @@ type
line*,column*: int
proc `$`*(arg: Lineinfo): string =
result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
# BUG: without `result = `, gives compile error
result = lineInfoToString(arg.filename, arg.line, arg.column)
#proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.}
## returns the position the node appears in the original source file
@ -352,7 +440,11 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
## copy lineinfo from info node
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
result.filename = n.getFile
result.line = n.getLine
result.column = n.getColumn
@ -464,9 +556,11 @@ proc newCall*(theProc: NimNode,
result.add(args)
proc newCall*(theProc: NimIdent,
args: varargs[NimNode]): NimNode {.compileTime.} =
args: varargs[NimNode]): NimNode {.compileTime, deprecated.} =
## produces a new call node. `theProc` is the proc that is called with
## the arguments ``args[0..]``.
## **Deprecated since version 0.18.1**; Use ``newCall(string, ...)``,
## or ``newCall(NimNode, ...)`` instead.
result = newNimNode(nnkCall)
result.add(newIdentNode(theProc))
result.add(args)
@ -594,17 +688,30 @@ proc newLit*(s: string): NimNode {.compileTime.} =
result = newNimNode(nnkStrLit)
result.strVal = s
proc nestList*(theProc: NimIdent,
x: NimNode): NimNode {.compileTime.} =
## nests the list `x` into a tree of call expressions:
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} =
## nests the list `pack` into a tree of call expressions:
## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
## This is also known as fold expression.
if pack.len < 1:
error("`nestList` expects a node with at least 1 child")
result = pack[^1]
for i in countdown(pack.len - 2, 0):
result = newCall(op, pack[i], result)
proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} =
## nests the list `pack` into a tree of call expressions:
## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
## This is also known as fold expression.
result = init
for i in countdown(pack.len - 1, 0):
result = newCall(op, pack[i], result)
proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated.} =
## **Deprecated since version 0.18.1**; Use one of ``nestList(NimNode, ...)`` instead.
var L = x.len
result = newCall(theProc, x[L-2], x[L-1])
for i in countdown(L-3, 0):
# XXX the 'copyNimTree' here is necessary due to a bug in the evaluation
# engine that would otherwise create an endless loop here. :-(
# This could easily user code and so should be fixed in evals.nim somehow.
result = newCall(theProc, x[i], copyNimTree(result))
result = newCall(theProc, x[i], result)
proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
## Convert the AST `n` to a human-readable tree-like string.
@ -616,13 +723,11 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
res.add(($n.kind).substr(3))
case n.kind
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: res.add(" nil")
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
of nnkIdent: res.add(" ident\"" & $n.ident & '"')
of nnkSym: res.add(" \"" & $n.symbol & '"')
of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
res.add(" " & $n.strVal.newLit.repr)
of nnkNone: assert false
else:
for j in 0..n.len-1:
@ -641,13 +746,11 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
add(result, "(")
case n.kind
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: add(result, "nil")
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "ident\"" & $n.ident & '"')
of nnkSym: add(result, $n.symbol)
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
add(result, n.strVal.newLit.repr)
of nnkNone: assert false
else:
if n.len > 0:
@ -678,54 +781,27 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## See also `repr`, `treeRepr`, and `lispRepr`.
const
NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone}
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} =
## Functions copied from strutils
proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} =
const
HexChars = "0123456789ABCDEF"
var
t = x
result = newString(len)
for j in countdown(len-1, 0):
result[j] = HexChars[int(t and 0xF)]
t = t shr 4
# handle negative overflow
if t == 0 and x < 0: t = -1
result = newStringOfCap(s.len + s.len shr 2)
result.add(prefix)
for c in items(s):
case c
of '\0'..'\31', '\128'..'\255':
add(result, "\\x")
add(result, toHex(ord(c), 2))
of '\\': add(result, "\\\\")
of '\'': add(result, "\\'")
of '\"': add(result, "\\\"")
else: add(result, c)
add(result, suffix)
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
for i in 0..level-1: res.add " "
if n.kind in NodeKinds:
res.add("new" & ($n.kind).substr(3) & "Node(")
elif n.kind in LitKinds:
res.add("newLit(")
elif n.kind == nnkNilLit:
res.add("newNilLit()")
else:
res.add($n.kind)
case n.kind
of nnkEmpty: discard
of nnkNilLit: res.add("nil")
of nnkEmpty, nnkNilLit: discard
of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
of nnkIdent: res.add(($n.ident).escape())
of nnkSym: res.add(($n.symbol).escape())
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
res.add(n.strVal.newLit.repr)
of nnkNone: assert false
else:
res.add(".newTree(")
@ -769,11 +845,10 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
## See `dumpTree`.
macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
## Deprecated.
## Deprecated. Use `dumpTree` instead.
macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr
## Deprecated.
## Deprecated. Use `dumpLisp` instead.
proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} =
## Create a new empty node
@ -1019,28 +1094,21 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
proc basename*(a: NimNode): NimNode {.compiletime, benign.}
proc `$`*(node: NimNode): string {.compileTime.} =
## Get the string of an identifier node
case node.kind
of nnkIdent:
result = $node.ident
of nnkPostfix:
result = $node.basename.ident & "*"
of nnkStrLit..nnkTripleStrLit:
result = node.basename.strVal & "*"
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
result = node.strVal
of nnkSym:
result = $node.symbol
of nnkOpenSymChoice, nnkClosedSymChoice:
result = $node[0]
of nnkAccQuoted:
result = $node[0]
of nnkCommentStmt:
result = node.strVal
else:
badNodeKind node.kind, "$"
proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.}
## Create a new ident node from a string
iterator items*(n: NimNode): NimNode {.inline.} =
@ -1131,40 +1199,57 @@ proc copy*(node: NimNode): NimNode {.compileTime.} =
## An alias for copyNimTree().
return node.copyNimTree()
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
# first char is case sensitive
if a[0] != b[0]: return 1
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
when defined(nimVmEqIdent):
proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.
proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``a`` can be an identifier or a symbol.
proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
## is the same as ``s``. Note that this is the preferred way to check! Most
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkIdent:
result = node.ident == toNimIdent s
of nnkSym:
result = eqIdent($node.symbol, s)
of nnkOpenSymChoice, nnkClosedSymChoice:
result = eqIdent($node[0], s)
else:
result = false
proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``b`` can be an identifier or a symbol.
proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``a`` and ``b`` can be an identifier or a symbol.
else:
# this procedure is optimized for native code, it should not be compiled to nimVM bytecode.
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
# first char is case sensitive
if a[0] != b[0]: return 1
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.
proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
## is the same as ``s``. Note that this is the preferred way to check! Most
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkSym, nnkIdent:
result = eqIdent(node.strVal, s)
of nnkOpenSymChoice, nnkClosedSymChoice:
result = eqIdent($node[0], s)
else:
result = false
proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
## Search nnkFormalParams for an argument.
@ -1215,33 +1300,81 @@ macro expandMacros*(body: typed): untyped =
echo result.toStrLit
proc customPragmaNode(n: NimNode): NimNode =
expectKind(n, {nnkSym, nnkDotExpr})
if n.kind == nnkSym:
let sym = n.symbol.getImpl()
sym.expectRoutine()
result = sym.pragma
elif n.kind == nnkDotExpr:
let typDef = getImpl(getTypeInst(n[0]).symbol)
typDef.expectKind(nnkTypeDef)
typDef[2].expectKind(nnkObjectTy)
let recList = typDef[2][2]
for identDefs in recList:
for i in 0 .. identDefs.len - 3:
if identDefs[i].kind == nnkPragmaExpr and
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]:
return identDefs[i][1]
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr})
let
typ = n.getTypeInst()
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
return typ[1][1]
elif typ.typeKind == ntyTypeDesc:
let impl = typ[1].getImpl()
if impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
return impl[0] # handle types which don't have macro at all
if n.kind == nnkSym: # either an variable or a proc
let impl = n.getImpl()
if impl.kind in RoutineNodes:
return impl.pragma
else:
return typ.getImpl()[0][1]
if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
let name = (if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
var typDef = getImpl(getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]))
while typDef != nil:
typDef.expectKind(nnkTypeDef)
typDef[2].expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy})
let isRef = typDef[2].kind in {nnkRefTy, nnkPtrTy}
if isRef and typDef[2][0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
typDef = getImpl(typDef[2][0])
else: # object definition, maybe an object directly defined as a ref type
let
obj = (if isRef: typDef[2][0] else: typDef[2])
var identDefsStack = newSeq[NimNode](obj[2].len)
for i in 0..<identDefsStack.len: identDefsStack[i] = obj[2][i]
while identDefsStack.len > 0:
var identDefs = identDefsStack.pop()
if identDefs.kind == nnkRecCase:
identDefsStack.add(identDefs[0])
for i in 1..<identDefs.len:
# if it is and empty branch, skip
if identDefs[i][0].kind == nnkNilLit: continue
if identDefs[i][1].kind == nnkIdentDefs:
identDefsStack.add(identDefs[i][1])
else: # nnkRecList
for j in 0..<identDefs[i][1].len:
identDefsStack.add(identDefs[i][1][j])
else:
for i in 0 .. identDefs.len - 3:
if identDefs[i].kind == nnkPragmaExpr and
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $name:
return identDefs[i][1]
if obj[1].kind == nnkOfInherit: # explore the parent object
typDef = getImpl(obj[1][0])
else:
typDef = nil
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
## has custom pragma `cp`.
## (if checking a field), a proc or a type has custom pragma `cp`.
##
## See also `getCustomPragmaVal`.
##
## .. code-block:: nim
## template myAttr() {.pragma.}
## type
## MyObj = object
## myField {.myAttr.}: int
##
## proc myProc() {.myAttr.} = discard
##
## var o: MyObj
## assert(o.myField.hasCustomPragma(myAttr) == 0)
## assert(o.myField.hasCustomPragma(myAttr))
## assert(myProc.hasCustomPragma(myAttr))
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if (p.kind == nnkSym and p == cp) or
@ -1251,20 +1384,25 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
## Expands to value of custom pragma `cp` of expression `n` which is expected
## to be `nnkDotExpr`.
## to be `nnkDotExpr`, a proc or a type.
##
## See also `hasCustomPragma`
##
## .. code-block:: nim
## template serializationKey(key: string) {.pragma.}
## type
## MyObj = object
## MyObj {.serializationKey: "mo".} = object
## myField {.serializationKey: "mf".}: int
## var o: MyObj
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
## assert(o.getCustomPragmaVal(serializationKey) == "mo")
## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
return p[1]
return newEmptyNode()
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
when not defined(booting):
@ -1285,3 +1423,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
result = newCall(callee)
for i in 0 ..< args.len:
result.add args[i]
proc getProjectPath*(): string = discard
## Returns the path to the currently compiling project

View file

@ -7,133 +7,163 @@
# distribution, for details about the copyright.
#
import allocators, typetraits
import typetraits
# strs already imported allocators for us.
## Default seq implementation used by Nim's core.
type
seq*[T] = object
len, cap: int
data: ptr UncheckedArray[T]
NimSeqPayload {.core.}[T] = object
cap: int
region: Allocator
data: UncheckedArray[T]
template frees(s) = dealloc(s.data, s.cap * sizeof(T))
NimSeqV2*[T] = object
len: int
p: ptr NimSeqPayload[T]
const nimSeqVersion {.core.} = 2
template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator)
# XXX make code memory safe for overflows in '*'
proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} =
seq[T](len: x.len, cap: x.len, data: x)
when defined(nimHasTrace):
proc `=trace`[T](s: seq[T]; a: Allocator) =
for i in 0 ..< s.len: `=trace`(s.data[i], a)
when false:
# this is currently not part of Nim's type bound operators and so it's
# built into the tracing proc generation just like before.
proc `=trace`[T](s: NimSeqV2[T]) =
for i in 0 ..< s.len: `=trace`(s.data[i])
proc `=destroy`[T](x: var seq[T]) =
if x.data != nil:
proc `=destroy`[T](s: var seq[T]) =
var x = cast[ptr NimSeqV2[T]](addr s)
var p = x.p
if p != nil:
when not supportsCopyMem(T):
for i in 0..<x.len: `=destroy`(x[i])
frees(x)
x.data = nil
for i in 0..<x.len: `=destroy`(p.data[i])
p.region.dealloc(p.region, p, payloadSize(p.cap))
x.p = nil
x.len = 0
x.cap = 0
proc `=`[T](a: var seq[T]; b: seq[T]) =
if a.data == b.data: return
if a.data != nil:
frees(a)
a.data = nil
proc `=`[T](x: var seq[T]; y: seq[T]) =
var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
if a.p == b.p: return
`=destroy`(a)
a.len = b.len
a.cap = b.cap
if b.data != nil:
a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
if b.p != nil:
a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
when supportsCopyMem(T):
copyMem(a.data, b.data, a.cap * sizeof(T))
if a.len > 0:
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
else:
for i in 0..<a.len:
a.data[i] = b.data[i]
a.p.data[i] = b.p.data[i]
proc `=sink`[T](a: var seq[T]; b: seq[T]) =
if a.data != nil and a.data != b.data:
frees(a)
proc `=sink`[T](x: var seq[T]; y: seq[T]) =
var a = cast[ptr NimSeqV2[T]](addr x)
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
if a.p != nil and a.p != b.p:
`=destroy`(a)
a.len = b.len
a.cap = b.cap
a.data = b.data
a.p = b.p
proc resize[T](s: var seq[T]) =
let old = s.cap
if old == 0: s.cap = 8
else: s.cap = (s.cap * 3) shr 1
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
when false:
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
proc reserveSlot[T](x: var seq[T]): ptr T =
if x.len >= x.cap: resize(x)
result = addr(x.data[x.len])
inc x.len
template add*[T](x: var seq[T]; y: T) =
reserveSlot(x)[] = y
type
PayloadBase = object
cap: int
region: Allocator
proc shrink*[T](x: var seq[T]; newLen: int) =
assert newLen <= x.len
assert newLen >= 0
proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
# we have to use type erasure here as Nim does not support generic
# compilerProcs. Oh well, this will all be inlined anyway.
if cap <= 0:
let region = getLocalAllocator()
var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator)))
p.region = region
p.cap = cap
result = p
else:
result = nil
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} =
if len+addlen <= len:
result = p
elif p == nil:
result = newSeqPayload(len+addlen, elemSize)
else:
# Note: this means we cannot support things that have internal pointers as
# they get reallocated here. This needs to be documented clearly.
var p = cast[ptr PayloadBase](p)
let region = if p.region == nil: getLocalAllocator() else: p.region
let cap = max(resize(p.cap), len+addlen)
var q = cast[ptr PayloadBase](region.realloc(region, p,
sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
sizeof(int) + sizeof(Allocator) + elemSize * cap))
q.region = region
q.cap = cap
result = q
proc shrink*[T](x: var seq[T]; newLen: Natural) =
sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
when not supportsCopyMem(T):
for i in countdown(x.len - 1, newLen - 1):
`=destroy`(x.data[i])
x.len = newLen
`=destroy`(x[i])
proc grow*[T](x: var seq[T]; newLen: int; value: T) =
if newLen <= x.len: return
assert newLen >= 0
if x.cap == 0: x.cap = newLen
else: x.cap = max(newLen, (x.cap * 3) shr 1)
x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
for i in x.len..<newLen:
cast[ptr NimSeqV2[T]](addr x).len = newLen
proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
let oldLen = x.len
if newLen <= oldLen: return
var xu = cast[ptr NimSeqV2[T]](addr x)
xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))
xu.len = newLen
for i in oldLen .. newLen-1:
x.data[i] = value
x.len = newLen
template default[T](t: typedesc[T]): T =
var v: T
v
proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
if newlen < x.len: shrink(x, newLen)
else: grow(x, newLen, default(T))
template `[]`*[T](x: seq[T]; i: Natural): T =
assert i < x.len
x.data[i]
template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
assert i < x.len
x.data[i] = y
proc `@`*[T](elems: openArray[T]): seq[T] =
result.cap = elems.len
result.len = elems.len
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
proc setLen[T](s: var seq[T], newlen: Natural) =
if newlen < s.len:
shrink(s, newLen)
else:
for i in 0..<result.len:
result.data[i] = elems[i]
var v: T # get the default value of 'v'
grow(s, newLen, v)
proc len*[T](x: seq[T]): int {.inline.} = x.len
when false:
proc resize[T](s: var NimSeqV2[T]) =
let old = s.cap
if old == 0: s.cap = 8
else: s.cap = (s.cap * 3) shr 1
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
proc `$`*[T](x: seq[T]): string =
result = "@["
var firstElement = true
for i in 0..<x.len:
let
value = x.data[i]
if firstElement:
firstElement = false
proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
if x.len >= x.cap: resize(x)
result = addr(x.data[x.len])
inc x.len
template add*[T](x: var NimSeqV2[T]; y: T) =
reserveSlot(x)[] = y
template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
assert i < x.len
x.data[i]
template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
assert i < x.len
x.data[i] = y
proc `@`*[T](elems: openArray[T]): NimSeqV2[T] =
result.cap = elems.len
result.len = elems.len
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
else:
result.add(", ")
when compiles(value.isNil):
# this branch should not be necessary
if value.isNil:
result.add "nil"
else:
result.addQuoted(value)
else:
result.addQuoted(value)
result.add("]")
for i in 0..<result.len:
result.data[i] = elems[i]

View file

@ -7,105 +7,166 @@
# distribution, for details about the copyright.
#
## Default string implementation used by Nim's core.
## Default new string implementation used by Nim's core.
when false:
# these are to be implemented or changed in the code generator.
#proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
# seems to be unused.
proc copyDeepString(src: NimString): NimString {.inline.}
# ----------------- sequences ----------------------------------------------
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.}
import allocators
type
string {.core.} = object
len, cap: int
data: ptr UncheckedArray[char]
NimStrPayload {.core.} = object
cap: int
region: Allocator
data: UncheckedArray[char]
proc nimStringLiteral(x: cstring; len: int): string {.core.} =
string(len: len, cap: len, data: x)
NimStringV2 {.core.} = object
len: int
p: ptr NimStrPayload ## can be nil if len == 0.
template frees(s) = dealloc(s.data, s.cap + 1)
const nimStrVersion {.core.} = 2
template isLiteral(s): bool = s.p == nil or s.p.region == nil
template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
template frees(s) =
if not isLiteral(s):
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
proc `=destroy`(s: var string) =
if s.data != nil:
frees(s)
s.data = nil
s.len = 0
s.cap = 0
var a = cast[ptr NimStringV2](addr s)
frees(a)
a.len = 0
a.p = nil
proc `=sink`(a: var string, b: string) =
template lose(a) =
frees(a)
proc `=sink`(x: var string, y: string) =
var a = cast[ptr NimStringV2](addr x)
var b = cast[ptr NimStringV2](unsafeAddr y)
# we hope this is optimized away for not yet alive objects:
if a.data != nil and a.data != b.data:
frees(a)
if unlikely(a.p == b.p): return
lose(a)
a.len = b.len
a.cap = b.cap
a.data = b.data
a.p = b.p
proc `=`(a: var string; b: string) =
if a.data != nil and a.data != b.data:
frees(a)
a.data = nil
proc `=`(x: var string, y: string) =
var a = cast[ptr NimStringV2](addr x)
var b = cast[ptr NimStringV2](unsafeAddr y)
if unlikely(a.p == b.p): return
lose(a)
a.len = b.len
a.cap = b.cap
if b.data != nil:
a.data = cast[type(a.data)](alloc(a.cap + 1))
copyMem(a.data, b.data, a.cap+1)
if isLiteral(b):
# we can shallow copy literals:
a.p = b.p
else:
let region = if a.p.region != nil: a.p.region else: getLocalAllocator()
# we have to allocate the 'cap' here, consider
# 'let y = newStringOfCap(); var x = y'
# on the other hand... These get turned into moves now.
a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
a.p.region = region
a.p.cap = b.len
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
proc resize(s: var string) =
let old = s.cap
if old == 0: s.cap = 8
else: s.cap = (s.cap * 3) shr 1
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
proc resize(old: int): int {.inline.} =
if old <= 0: result = 4
elif old < 65536: result = old * 2
else: result = old * 3 div 2 # for large arrays * 3/2 is better
proc add*(s: var string; c: char) =
if s.len >= s.cap: resize(s)
s.data[s.len] = c
s.data[s.len+1] = '\0'
proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
if isLiteral(s):
let oldP = s.p
# can't mutate a literal, so we need a fresh copy here:
let region = getLocalAllocator()
s.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(s.len + addlen)))
s.p.region = region
s.p.cap = s.len + addlen
if s.len > 0:
# we are about to append, so there is no need to copy the \0 terminator:
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
elif s.len + addlen > s.p.cap:
let cap = max(s.len + addlen, resize(s.p.cap))
s.p = cast[ptr NimStrPayload](s.p.region.realloc(s.p.region, s.p,
oldSize = contentSize(s.p.cap),
newSize = contentSize(cap)))
s.p.cap = cap
proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
prepareAdd(s, 1)
s.p.data[s.len] = c
s.p.data[s.len+1] = '\0'
inc s.len
proc ensure(s: var string; newLen: int) =
let old = s.cap
if newLen >= old:
s.cap = max((old * 3) shr 1, newLen)
if s.cap > 0:
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
if len <= 0:
result = NimStringV2(len: 0, p: nil)
else:
let region = getLocalAllocator()
var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
p.region = region
p.cap = len
if len > 0:
# we are about to append, so there is no need to copy the \0 terminator:
copyMem(unsafeAddr p.data[0], str, len)
result = NimStringV2(len: 0, p: p)
proc add*(s: var string; y: string) =
if y.len != 0:
let newLen = s.len + y.len
ensure(s, newLen)
copyMem(addr s.data[len], y.data, y.data.len + 1)
proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
if str == nil: toNimStr(str, 0)
else: toNimStr(str, str.len)
proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, inline.} =
if s.len == 0: result = cstring""
else: result = cstring(unsafeAddr s.p.data)
proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
if src.len > 0:
# also copy the \0 terminator:
copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
dest.p.data[dest.len] = c
dest.p.data[dest.len+1] = '\0'
inc dest.len
proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
# this is also 'system.newStringOfCap'.
if space <= 0:
result = NimStringV2(len: 0, p: nil)
else:
let region = getLocalAllocator()
var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(space)))
p.region = region
p.cap = space
result = NimStringV2(len: 0, p: p)
proc mnewString(len: int): NimStringV2 {.compilerProc.} =
if len <= 0:
result = NimStringV2(len: 0, p: nil)
else:
let region = getLocalAllocator()
var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
p.region = region
p.cap = len
result = NimStringV2(len: len, p: p)
proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
if newLen > s.len:
prepareAdd(s, newLen - s.len)
else:
s.len = newLen
proc len*(s: string): int {.inline.} = s.len
proc newString*(len: int): string =
result.len = len
result.cap = len
if len > 0:
result.data = alloc0(len+1)
converter toCString(x: string): cstring {.core.} =
if x.len == 0: cstring"" else: cast[cstring](x.data)
proc newStringOfCap*(cap: int): string =
result.len = 0
result.cap = cap
if cap > 0:
result.data = alloc(cap+1)
proc `&`*(a, b: string): string =
let sum = a.len + b.len
result = newStringOfCap(sum)
result.len = sum
copyMem(addr result.data[0], a.data, a.len)
copyMem(addr result.data[a.len], b.data, b.len)
if sum > 0:
result.data[sum] = '\0'
proc concat(x: openArray[string]): string {.core.} =
## used be the code generator to optimize 'x & y & z ...'
var sum = 0
for i in 0 ..< x.len: inc(sum, x[i].len)
result = newStringOfCap(sum)
sum = 0
for i in 0 ..< x.len:
let L = x[i].len
copyMem(addr result.data[sum], x[i].data, L)
inc(sum, L)
# this also only works because the destructor
# looks at s.p and not s.len

View file

@ -43,7 +43,6 @@ type
t: Thread[ptr Actor[In, Out]]
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
{.deprecated: [TTask: Task, TActor: Actor].}
proc spawn*[In, Out](action: proc(
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
@ -168,7 +167,7 @@ proc terminate*[In, Out](a: var ActorPool[In, Out]) =
for i in 0..<a.actors.len: join(a.actors[i])
when Out isnot void:
close(a.outputs)
a.actors = nil
a.actors = @[]
proc join*[In, Out](a: var ActorPool[In, Out]) =
## short-cut for `sync` and then `terminate`.

View file

@ -101,8 +101,8 @@ when defined(windows):
from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
FD_ISSET, select
else:
from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
FD_ISSET, select
from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO,
TFdSet, FD_ISSET, select
type
DelegateObj* = object
@ -272,7 +272,7 @@ proc asyncSockHandleWrite(h: RootRef) =
AsyncSocket(h).deleg.mode = fmRead
when defined(ssl):
proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
proc asyncSockDoHandshake(h: RootRef) {.gcsafe.} =
if AsyncSocket(h).socket.isSSL and not
AsyncSocket(h).socket.gotHandshake:
if AsyncSocket(h).sslNeedAccept:
@ -556,8 +556,12 @@ proc send*(sock: AsyncSocket, data: string) =
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
when defined(posix):
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
else:
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
FD_ZERO(fd)

View file

@ -12,7 +12,7 @@ import sockets, strutils, parseutils, times, os, asyncio
from asyncnet import nil
from nativesockets import nil
from asyncdispatch import PFuture
from asyncdispatch import Future
## **Note**: This module is deprecated since version 0.11.3.
## You should use the async version of this module
## `asyncftpclient <asyncftpclient.html>`_.

View file

@ -32,10 +32,12 @@
include "system/inclrtl"
{.deadCodeElim: on.}
{.deadCodeElim: on.} # dce option deprecated
when hostOS == "solaris":
{.passl: "-lsocket -lnsl".}
elif hostOS == "haiku":
{.passl: "-lnetwork".}
import os, parseutils
from times import epochTime
@ -953,8 +955,12 @@ when defined(ssl):
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
when defined(posix):
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
else:
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
FD_ZERO(fd)

View file

@ -8,10 +8,15 @@
#
# Low level dataspace allocator for Genode.
# For interacting with dataspaces outside of the
# standard library see the Genode Nimble package.
when not defined(genode):
{.error: "Genode only module".}
when not declared(GenodeEnv):
include genode/env
type DataspaceCapability {.
importcpp: "Genode::Dataspace_capability", pure.} = object
@ -31,35 +36,35 @@ type
const SlabBackendSize = 4096
proc ramAvail(): int {.
importcpp: "genodeEnv->pd().avail_ram().value".}
proc ramAvail(env: GenodeEnv): int {.
importcpp: "#->pd().avail_ram().value".}
## Return number of bytes available for allocation.
proc capsAvail(): int {.
importcpp: "genodeEnv->pd().avail_caps().value".}
proc capsAvail(env: GenodeEnv): int {.
importcpp: "#->pd().avail_caps().value".}
## Return the number of available capabilities.
## Each dataspace allocation consumes a capability.
proc allocDataspace(size: int): DataspaceCapability {.
importcpp: "genodeEnv->pd().alloc(@)".}
proc allocDataspace(env: GenodeEnv; size: int): DataspaceCapability {.
importcpp: "#->pd().alloc(@)".}
## Allocate a dataspace and its capability.
proc attachDataspace(ds: DataspaceCapability): pointer {.
importcpp: "genodeEnv->rm().attach(@)".}
proc attachDataspace(env: GenodeEnv; ds: DataspaceCapability): pointer {.
importcpp: "#->rm().attach(@)".}
## Attach a dataspace into the component address-space.
proc detachAddress(p: pointer) {.
importcpp: "genodeEnv->rm().detach(@)".}
proc detachAddress(env: GenodeEnv; p: pointer) {.
importcpp: "#->rm().detach(@)".}
## Detach a dataspace from the component address-space.
proc freeDataspace(ds: DataspaceCapability) {.
importcpp: "genodeEnv->pd().free(@)".}
proc freeDataspace(env: GenodeEnv; ds: DataspaceCapability) {.
importcpp: "#->pd().free(@)".}
## Free a dataspace.
proc newMapSlab(): ptr MapSlab =
let
ds = allocDataspace SlabBackendSize
p = attachDataspace ds
ds = runtimeEnv.allocDataspace SlabBackendSize
p = runtimeEnv.attachDataspace ds
result = cast[ptr MapSlab](p)
result.meta.ds = ds
@ -89,13 +94,13 @@ proc osAllocPages(size: int): pointer =
# tack a new slab on the tail
slab = slab.meta.next
# move to next slab in linked list
map.ds = allocDataspace size
map.ds = runtimeEnv.allocDataspace size
map.size = size
map.attachment = attachDataspace map.ds
map.attachment = runtimeEnv.attachDataspace map.ds
result = map.attachment
proc osTryAllocPages(size: int): pointer =
if ramAvail() >= size and capsAvail() > 1:
if runtimeEnv.ramAvail() >= size and runtimeEnv.capsAvail() > 4:
result = osAllocPages size
proc osDeallocPages(p: pointer; size: int) =
@ -107,8 +112,8 @@ proc osDeallocPages(p: pointer; size: int) =
if m.size != size:
echo "cannot partially detach dataspace"
quit -1
detachAddress m.attachment
freeDataspace m.ds
runtimeEnv.detachAddress m.attachment
runtimeEnv.freeDataspace m.ds
m[] = Map()
return
slab = slab.meta.next

29
lib/genode/env.nim Normal file
View file

@ -0,0 +1,29 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2018 Emery Hemingway
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
#
# This file contains the minimum required definitions
# for interacting with the initial Genode environment.
# It is reserved for use only within the standard
# library. See ``componentConstructHook`` in the system
# module for accessing the Genode environment after the
# standard library has finished initializating.
#
when not defined(genode):
{.error: "Genode only include".}
type
GenodeEnvObj {.importcpp: "Genode::Env", header: "<base/env.h>", pure.} = object
GenodeEnvPtr = ptr GenodeEnvObj
const runtimeEnvSym = "nim_runtime_env"
when not defined(nimscript):
var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr

View file

@ -13,6 +13,7 @@
#define _GENODE_CPP__THREAD_H_
#include <base/thread.h>
#include <base/env.h>
#include <util/reconstructible.h>
namespace Nim { struct SysThread; }

View file

@ -128,10 +128,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
var a = 0
for c in items(string(formatstr)):
if c == '?':
if args[a] == nil:
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
@ -183,24 +180,8 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
row = mysql.fetchRow(sqlres)
if row == nil: break
for i in 0..L-1:
if row[i] == nil:
if backup == nil:
newSeq(backup, L)
if backup[i] == nil and result[i] != nil:
shallowCopy(backup[i], result[i])
result[i] = nil
else:
if result[i] == nil:
if backup != nil:
if backup[i] == nil:
backup[i] = ""
shallowCopy(result[i], backup[i])
setLen(result[i], 0)
else:
result[i] = ""
else:
setLen(result[i], 0)
add(result[i], row[i])
setLen(result[i], 0)
result[i].add row[i]
yield result
properFreeResult(sqlres, row)
@ -323,10 +304,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
if row != nil:
for i in 0..L-1:
setLen(result[i], 0)
if row[i] == nil:
result[i] = nil
else:
add(result[i], row[i])
add(result[i], row[i])
properFreeResult(sqlres, row)
proc getAllRows*(db: DbConn, query: SqlQuery,
@ -345,10 +323,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
setLen(result, j+1)
newSeq(result[j], L)
for i in 0..L-1:
if row[i] == nil:
result[j][i] = nil
else:
result[j][i] = $row[i]
result[j][i] = $row[i]
inc(j)
mysql.freeResult(sqlres)

View file

@ -103,10 +103,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
else:
for c in items(string(formatstr)):
if c == '?':
if args[a] == nil:
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
@ -179,7 +176,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
setLen(r[col], 0)
let x = pqgetvalue(res, line, col)
if x.isNil:
r[col] = nil
r[col] = ""
else:
add(r[col], x)

View file

@ -31,7 +31,7 @@
##
## .. code-block:: Nim
## import db_sqlite
## let db = open("localhost", "user", "password", "dbname")
## let db = open("mytest.db", nil, nil, nil) # user, password, database name can be nil
## db.close()
##
## Creating a table
@ -57,7 +57,7 @@
##
## import db_sqlite, math
##
## let theDb = open("mytest.db", nil, nil, nil)
## let theDb = open("mytest.db", "", "", "")
##
## theDb.exec(sql"Drop table if exists myTestTbl")
## theDb.exec(sql("""create table myTestTbl (
@ -81,7 +81,7 @@
##
## theDb.close()
{.deadCodeElim:on.}
{.deadCodeElim: on.} # dce option deprecated
import strutils, sqlite3
@ -105,7 +105,6 @@ proc dbError*(db: DbConn) {.noreturn.} =
proc dbQuote*(s: string): string =
## DB quotes the string.
if s.isNil: return "NULL"
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
@ -126,6 +125,7 @@ proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
@ -144,6 +144,7 @@ proc newRow(L: int): Row =
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt =
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
@ -267,6 +268,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
{.tags: [WriteDbEffect], raises: [].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt
result = -1

View file

@ -10,7 +10,7 @@
from pcre import nil
import nre.private.util
import tables
from strutils import toLower, `%`
from strutils import `%`
from math import ceil
import options
from unicode import runeLenAt
@ -267,7 +267,7 @@ proc `[]`*(pattern: Captures, i: int): string =
let bounds = bounds.get
return pattern.str.substr(bounds.a, bounds.b)
else:
return nil
return ""
proc match*(pattern: RegexMatch): string =
return pattern.captures[-1]
@ -291,9 +291,9 @@ template toTableImpl(cond: untyped) {.dirty.} =
else:
result[key] = nextVal
proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
proc toTable*(pattern: Captures, default: string = ""): Table[string, string] =
result = initTable[string, string]()
toTableImpl(nextVal == nil)
toTableImpl(nextVal.len == 0)
proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
Table[string, Option[HSlice[int, int]]] =
@ -312,13 +312,13 @@ template itemsImpl(cond: untyped) {.dirty.} =
iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
itemsImpl(nextVal.isNone)
iterator items*(pattern: Captures, default: string = nil): string =
itemsImpl(nextVal == nil)
iterator items*(pattern: Captures, default: string = ""): string =
itemsImpl(nextVal.len == 0)
proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
accumulateResult(pattern.items(default))
proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
proc toSeq*(pattern: Captures, default: string = ""): seq[string] =
accumulateResult(pattern.items(default))
proc `$`*(pattern: RegexMatch): string =
@ -326,15 +326,15 @@ proc `$`*(pattern: RegexMatch): string =
proc `==`*(a, b: Regex): bool =
if not a.isNil and not b.isNil:
return a.pattern == b.pattern and
a.pcreObj == b.pcreObj and
return a.pattern == b.pattern and
a.pcreObj == b.pcreObj and
a.pcreExtra == b.pcreExtra
else:
return system.`==`(a, b)
proc `==`*(a, b: RegexMatch): bool =
return a.pattern == b.pattern and
a.str == b.str
a.str == b.str
# }}}
# Creation & Destruction {{{
@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
let bounds = match.matchBounds
result.add(str.substr(lastIdx, bounds.a - 1))
let nextVal = replacement
assert(nextVal != nil)
result.add(nextVal)
lastIdx = bounds.b + 1
@ -655,17 +654,17 @@ template replaceImpl(str: string, pattern: Regex,
proc replace*(str: string, pattern: Regex,
subproc: proc (match: RegexMatch): string): string =
## Replaces each match of Regex in the string with ``sub``, which should
## Replaces each match of Regex in the string with ``subproc``, which should
## never be or return ``nil``.
##
## If ``sub`` is a ``proc (RegexMatch): string``, then it is executed with
## If ``subproc`` is a ``proc (RegexMatch): string``, then it is executed with
## each match and the return value is the replacement value.
##
## If ``sub`` is a ``proc (string): string``, then it is executed with the
## If ``subproc`` is a ``proc (string): string``, then it is executed with the
## full text of the match and and the return value is the replacement
## value.
##
## If ``sub`` is a string, the syntax is as follows:
## If ``subproc`` is a string, the syntax is as follows:
##
## - ``$$`` - literal ``$``
## - ``$123`` - capture number ``123``

View file

@ -10,11 +10,7 @@ proc fget*[K, V](self: Table[K, V], key: K): V =
const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
const StartIdent = Ident - {'0'..'9'}
proc checkNil(arg: string): string =
if arg == nil:
raise newException(ValueError, "Cannot use nil capture")
else:
return arg
template checkNil(arg: string): string = arg
template formatStr*(howExpr, namegetter, idgetter): untyped =
let how = howExpr

View file

@ -13,7 +13,7 @@
## is used. This suffices because Windows' console already provides the
## wanted functionality.
{.deadCodeElim: on.}
{.deadCodeElim: on.} # dce option deprecated
when defined(Windows):
proc readLineFromStdin*(prompt: string): TaintedString {.

View file

@ -7,18 +7,14 @@
# distribution, for details about the copyright.
#
## Regular expression support for Nim. This module still has some
## obscure bugs and limitations,
## consider using the ``nre`` or ``pegs`` modules instead.
## We had to de-deprecate this module since too much code relies on it
## and many people prefer its API over ``nre``'s.
## Regular expression support for Nim.
##
## This module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PRCE
## `PCRE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PCRE
## library's licence when using this module, which should not be a problem
## though.
## PRCE's licence follows:
## PCRE's licence follows:
##
## .. include:: ../../doc/regexprs.txt
##
@ -117,7 +113,7 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
var b = rawMatches[i * 2 + 1]
if a >= 0'i32:
matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil
else: matches[i-1] = ""
return rawMatches[1] - rawMatches[0]
proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
@ -137,7 +133,7 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
var a = rawMatches[i * 2]
var b = rawMatches[i * 2 + 1]
if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil
else: matches[i-1] = ""
return (rawMatches[0].int, rawMatches[1].int - 1)
proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
@ -291,7 +287,7 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
var a = rawMatches[i * 2]
var b = rawMatches[i * 2 + 1]
if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil
else: matches[i-1] = ""
return rawMatches[0]
proc find*(s: string, pattern: Regex, matches: var openArray[string],
@ -460,8 +456,6 @@ proc replacef*(s: string, sub: Regex, by: string): string =
while true:
var match = findBounds(s, sub, caps, prev)
if match.first < 0: break
assert result != nil
assert s != nil
add(result, substr(s, prev, match.first-1))
addf(result, by, caps)
prev = match.last + 1
@ -502,7 +496,7 @@ proc transformFile*(infile, outfile: string,
var x = readFile(infile).string
writeFile(outfile, x.multiReplace(subs))
iterator split*(s: string, sep: Regex): string =
iterator split*(s: string, sep: Regex; maxsplit = -1): string =
## Splits the string ``s`` into substrings.
##
## Substrings are separated by the regular expression ``sep``
@ -524,22 +518,28 @@ iterator split*(s: string, sep: Regex): string =
## "example"
## ""
##
var
first = -1
last = -1
while last < len(s):
var x = matchLen(s, sep, last)
if x > 0: inc(last, x)
first = last
if x == 0: inc(last)
var last = 0
var splits = maxsplit
var x: int
while last <= len(s):
var first = last
var sepLen = 1
while last < len(s):
x = matchLen(s, sep, last)
if x >= 0: break
if x >= 0:
sepLen = x
break
inc(last)
if first <= last:
yield substr(s, first, last-1)
if x == 0:
if last >= len(s): break
inc last
if splits == 0: last = len(s)
yield substr(s, first, last-1)
if splits == 0: break
dec(splits)
inc(last, sepLen)
proc split*(s: string, sep: Regex): seq[string] {.inline.} =
proc split*(s: string, sep: Regex, maxsplit = -1): seq[string] {.inline.} =
## Splits the string ``s`` into a seq of substrings.
##
## The portion matched by ``sep`` is not returned.
@ -613,7 +613,7 @@ when isMainModule:
doAssert false
if "abc" =~ re"(cba)?.*":
doAssert matches[0] == nil
doAssert matches[0] == ""
else: doAssert false
if "abc" =~ re"().*":
@ -636,6 +636,14 @@ when isMainModule:
accum.add(word)
doAssert(accum == @["AAA", "", "BBB"])
doAssert(split("abc", re"") == @["a", "b", "c"])
doAssert(split("", re"") == @[])
doAssert(split("a;b;c", re";") == @["a", "b", "c"])
doAssert(split(";a;b;c", re";") == @["", "a", "b", "c"])
doAssert(split(";a;b;c;", re";") == @["", "a", "b", "c", ""])
doAssert(split("a;b;c;", re";") == @["a", "b", "c", ""])
for x in findAll("abcdef", re"^{.}", 3):
doAssert x == "d"
accum = @[]

View file

@ -92,7 +92,10 @@ proc isFutureVoid(node: NimNode): bool =
node[1].kind == nnkIdent and $node[1] == "void"
proc generateJsasync(arg: NimNode): NimNode =
assert arg.kind == nnkProcDef
if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
error("Cannot transform this node kind into an async proc." &
" proc/method definition or lambda node expected.")
result = arg
var isVoid = false
let jsResolve = ident("jsResolve")
@ -108,7 +111,7 @@ proc generateJsasync(arg: NimNode): NimNode =
if len(code) > 0:
var awaitFunction = quote:
proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
proc await[T](f: Future[T]): T {.importcpp: "(await #)", used.}
result.body.add(awaitFunction)
var resolve: NimNode
@ -117,7 +120,7 @@ proc generateJsasync(arg: NimNode): NimNode =
var `jsResolve` {.importcpp: "undefined".}: Future[void]
else:
resolve = quote:
proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
proc jsResolve[T](a: T): Future[T] {.importcpp: "#", used.}
result.body.add(resolve)
else:
result.body = newEmptyNode()
@ -129,14 +132,20 @@ proc generateJsasync(arg: NimNode): NimNode =
return `jsResolve`
result.body.add(voidFix)
result.pragma = quote:
let asyncPragma = quote:
{.codegenDecl: "async function $2($3)".}
result.addPragma(asyncPragma[0])
macro async*(arg: untyped): untyped =
## Macro which converts normal procedures into
## javascript-compatible async procedures
generateJsasync(arg)
if arg.kind == nnkStmtList:
result = newStmtList()
for oneProc in arg:
result.add generateJsasync(oneProc)
else:
result = generateJsasync(arg)
proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".}
## A helper for wrapping callback-based functions

View file

@ -45,6 +45,7 @@ type
location*: Location
closed*: bool
defaultStatus*: cstring
devicePixelRatio*: float
innerHeight*, innerWidth*: int
locationbar*: ref TLocationBar
menubar*: ref TMenuBar
@ -53,11 +54,15 @@ type
pageXOffset*, pageYOffset*: int
personalbar*: ref TPersonalBar
scrollbars*: ref TScrollBars
scrollX*: float
scrollY*: float
statusbar*: ref TStatusBar
status*: cstring
toolbar*: ref TToolBar
frames*: seq[TFrame]
screen*: Screen
performance*: Performance
onpopstate*: proc (event: Event)
Frame* = ref FrameObj
FrameObj {.importc.} = object of WindowObj
@ -171,6 +176,12 @@ type
text*: cstring
value*: cstring
TextAreaElement* = ref object of ElementObj
value*: cstring
selectionStart*, selectionEnd*: int
selectionDirection*: cstring
rows*, cols*: int
FormElement* = ref FormObj
FormObj {.importc.} = object of ElementObj
action*: cstring
@ -190,7 +201,7 @@ type
vspace*: int
width*: int
Style = ref StyleObj
Style* = ref StyleObj
StyleObj {.importc.} = object of RootObj
background*: cstring
backgroundAttachment*: cstring
@ -253,6 +264,8 @@ type
minHeight*: cstring
minWidth*: cstring
overflow*: cstring
overflowX*: cstring
overflowY*: cstring
padding*: cstring
paddingBottom*: cstring
paddingLeft*: cstring
@ -400,12 +413,47 @@ type
once*: bool
passive*: bool
BoundingRect* {.importc.} = ref object
top*, bottom*, left*, right*, x*, y*, width*, height*: float
PerformanceMemory* {.importc.} = ref object
jsHeapSizeLimit*: float
totalJSHeapSize*: float
usedJSHeapSize*: float
PerformanceTiming* {.importc.} = ref object
connectStart*: float
domComplete*: float
domContentLoadedEventEnd*: float
domContentLoadedEventStart*: float
domInteractive*: float
domLoading*: float
domainLookupEnd*: float
domainLookupStart*: float
fetchStart*: float
loadEventEnd*: float
loadEventStart*: float
navigationStart*: float
redirectEnd*: float
redirectStart*: float
requestStart*: float
responseEnd*: float
responseStart*: float
secureConnectionStart*: float
unloadEventEnd*: float
unloadEventStart*: float
Performance* {.importc.} = ref object
memory*: PerformanceMemory
timing*: PerformanceTiming
{.push importcpp.}
# EventTarget "methods"
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc dispatchEvent*(et: EventTarget, ev: Event)
# Window "methods"
proc alert*(w: Window, msg: cstring)
@ -451,6 +499,7 @@ proc cloneNode*(n: Node, copyContent: bool): Node
proc deleteData*(n: Node, start, len: int)
proc getAttribute*(n: Node, attr: cstring): cstring
proc getAttributeNode*(n: Node, attr: cstring): Node
proc getBoundingClientRect*(e: Node): BoundingRect
proc hasChildNodes*(n: Node): bool
proc insertBefore*(n, newNode, before: Node)
proc insertData*(n: Node, position: int, data: cstring)
@ -459,7 +508,7 @@ proc removeAttributeNode*(n, attr: Node)
proc removeChild*(n, child: Node)
proc replaceChild*(n, newNode, oldNode: Node)
proc replaceData*(n: Node, start, len: int, text: cstring)
proc scrollIntoView*(n: Node)
proc scrollIntoView*(n: Node, alignToTop: bool=true)
proc setAttribute*(n: Node, name, value: cstring)
proc setAttributeNode*(n: Node, attr: Node)
@ -530,6 +579,9 @@ proc preventDefault*(ev: Event)
proc identifiedTouch*(list: TouchList): Touch
proc item*(list: TouchList, i: int): Touch
# Performance "methods"
proc now*(p: Performance): float
{.pop.}
var
@ -552,6 +604,7 @@ proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.}
proc parseInt*(s: cstring): int {.importc, nodecl.}
proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
type
TEventHandlers* {.deprecated.} = EventTargetObj

96
lib/js/jscore.nim Normal file
View file

@ -0,0 +1,96 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2018 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module wraps core JavaScript functions.
##
## Unless your application has very
## specific requirements and solely targets JavaScript, you should be using
## the relevant functions in the ``math``, ``json``, and ``times`` stdlib
## modules instead.
when not defined(js) and not defined(Nimdoc):
{.error: "This module only works on the JavaScript platform".}
type
MathLib* = ref object
JsonLib* = ref object
DateLib* = ref object
DateTime* = ref object
var
Math* {.importc, nodecl.}: MathLib
Date* {.importc, nodecl.}: DateLib
JSON* {.importc, nodecl.}: JsonLib
# Math library
proc abs*(m: MathLib, a: SomeNumber): SomeNumber {.importcpp.}
proc acos*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc acosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc asin*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc asinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc atan*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc atan2*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc atanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
proc ceil*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
proc clz32*(m: MathLib, f: SomeInteger): int {.importcpp.}
proc cos*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc cosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc exp*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc expm1*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc floor*(m: MathLib, f: SomeFloat): int {.importcpp.}
proc fround*(m: MathLib, f: SomeFloat): float32 {.importcpp.}
proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float {.importcpp.}
proc imul*(m: MathLib, a, b: int32): int32 {.importcpp.}
proc log*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc log10*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc log1p*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc log2*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc max*(m: MathLib, a, b: SomeNumber): SomeNumber {.importcpp.}
proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T {.importcpp.}
proc pow*(m: MathLib, a, b: distinct SomeNumber): float {.importcpp.}
proc random*(m: MathLib): float {.importcpp.}
proc round*(m: MathLib, f: SomeFloat): int {.importcpp.}
proc sign*(m: MathLib, f: SomeNumber): int {.importcpp.}
proc sin*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc sinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
proc tan*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc tanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc trunc*(m: MathLib, f: SomeFloat): int {.importcpp.}
# Date library
proc now*(d: DateLib): int {.importcpp.}
proc UTC*(d: DateLib): int {.importcpp.}
proc parse*(d: DateLib, s: cstring): int {.importcpp.}
proc newDate*(): DateTime {.
importcpp: "new Date()".}
proc newDate*(date: int|string): DateTime {.
importcpp: "new Date(#)".}
proc newDate*(year, month, day, hours, minutes,
seconds, milliseconds: int): DateTime {.
importcpp: "new Date(#,#,#,#,#,#,#)".}
proc getDay*(d: DateTime): int {.importcpp.}
proc getFullYear*(d: DateTime): int {.importcpp.}
proc getHours*(d: DateTime): int {.importcpp.}
proc getMilliseconds*(d: DateTime): int {.importcpp.}
proc getMinutes*(d: DateTime): int {.importcpp.}
proc getMonth*(d: DateTime): int {.importcpp.}
proc getSeconds*(d: DateTime): int {.importcpp.}
proc getYear*(d: DateTime): int {.importcpp.}
proc getTime*(d: DateTime): int {.importcpp.}
proc toString*(d: DateTime): cstring {.importcpp.}
#JSON library
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.}

View file

@ -70,22 +70,29 @@ template mangleJsName(name: cstring): cstring =
"mangledName" & $nameCounter
type
JsRoot* = ref object of RootObj
## Root type of both JsObject and JsAssoc
JsObject* = ref object of JsRoot
## Dynamically typed wrapper around a JavaScript object.
JsAssoc*[K, V] = ref object of JsRoot
## Statically typed wrapper around a JavaScript object.
NotString = concept c
c isnot string
js* = JsObject
var jsarguments* {.importc: "arguments", nodecl}: JsObject
## JavaScript's arguments pseudo-variable
var
jsArguments* {.importc: "arguments", nodecl}: JsObject
## JavaScript's arguments pseudo-variable
jsNull* {.importc: "null", nodecl.}: JsObject
## JavaScript's null literal
jsUndefined* {.importc: "undefined", nodecl.}: JsObject
## JavaScript's undefined literal
jsDirname* {.importc: "__dirname", nodecl.}: cstring
## JavaScript's __dirname pseudo-variable
jsFilename* {.importc: "__filename", nodecl.}: cstring
## JavaScript's __filename pseudo-variable
# New
proc newJsObject*: JsObject {. importcpp: "{@}" .}
## Creates a new empty JsObject
proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .}
## Creates a new empty JsAssoc with key type `K` and value type `V`.
@ -97,13 +104,16 @@ proc hasOwnProperty*(x: JsObject, prop: cstring): bool
proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .}
## Returns the name of the JsObject's JavaScript type as a cstring.
proc jsnew*(x: auto): JsObject {.importcpp: "(new #)".}
proc jsNew*(x: auto): JsObject {.importcpp: "(new #)".}
## Turns a regular function call into an invocation of the
## JavaScript's `new` operator
proc jsdelete*(x: auto): JsObject {.importcpp: "(delete #)".}
proc jsDelete*(x: auto): JsObject {.importcpp: "(delete #)".}
## JavaScript's `delete` operator
proc require*(module: cstring): JsObject {.importc.}
## JavaScript's `require` function
# Conversion to and from JsObject
proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .}
## Converts a JsObject `x` to type `T`.
@ -155,7 +165,7 @@ proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .}
proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .}
## Set the value of a property of name `field` in a JsObject `obj` to `v`.
proc `[]`*[K: NotString, V](obj: JsAssoc[K, V], field: K): V
proc `[]`*[K: not string, V](obj: JsAssoc[K, V], field: K): V
{. importcpp: getImpl .}
## Return the value of a property of name `field` from a JsAssoc `obj`.
@ -163,7 +173,7 @@ proc `[]`*[V](obj: JsAssoc[string, V], field: cstring): V
{. importcpp: getImpl .}
## Return the value of a property of name `field` from a JsAssoc `obj`.
proc `[]=`*[K: NotString, V](obj: JsAssoc[K, V], field: K, val: V)
proc `[]=`*[K: not string, V](obj: JsAssoc[K, V], field: K, val: V)
{. importcpp: setImpl .}
## Set the value of a property of name `field` in a JsAssoc `obj` to `v`.

View file

@ -107,6 +107,8 @@ __clang__
# define N_INLINE(rettype, name) rettype __inline name
#endif
#define N_INLINE_PTR(rettype, name) rettype (*name)
#if defined(__POCC__)
# define NIM_CONST /* PCC is really picky with const modifiers */
# undef _MSC_VER /* Yeah, right PCC defines _MSC_VER even if it is
@ -129,13 +131,13 @@ __clang__
defined __DMC__ || \
defined __BORLANDC__ )
# define NIM_THREADVAR __declspec(thread)
#elif defined(__TINYC__) || defined(__GENODE__)
# define NIM_THREADVAR
/* note that ICC (linux) and Clang are covered by __GNUC__ */
#elif defined __GNUC__ || \
defined __SUNPRO_C || \
defined __xlC__
# define NIM_THREADVAR __thread
#elif defined __TINYC__
# define NIM_THREADVAR
#else
# error "Cannot define NIM_THREADVAR"
#endif
@ -264,6 +266,11 @@ __clang__
# define HAVE_STDINT_H
#endif
/* wrap all Nim typedefs into namespace Nim */
#ifdef USE_NIM_NAMESPACE
namespace Nim {
#endif
/* bool types (C++ has it): */
#ifdef __cplusplus
# ifndef NIM_TRUE
@ -413,8 +420,8 @@ typedef struct TStringDesc* string;
# endif
#endif
typedef struct TFrame TFrame;
struct TFrame {
typedef struct TFrame_ TFrame;
struct TFrame_ {
TFrame* prev;
NCSTRING procname;
NI line;
@ -476,6 +483,10 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
"error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */
typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#ifdef USE_NIM_NAMESPACE
}
#endif
#ifdef __cplusplus
# define NIM_EXTERNC extern "C"
#else
@ -493,11 +504,6 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
# include <sys/types.h>
#endif
#if defined(__GENODE__)
#include <libc/component.h>
extern Libc::Env *genodeEnv;
#endif
/* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
#define NIM_CHECK_SIZE(typ, sz) \
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")

View file

@ -0,0 +1,36 @@
## All of these library headers and source can be found in the github repo
## https://github.com/switchbrew/libnx.
const virtMemHeader = "<switch/kernel/virtmem.h>"
const svcHeader = "<switch/kernel/svc.h>"
const mallocHeader = "<malloc.h>"
## Aligns a block of memory with request `size` to `bytes` size. For
## example, a request of memalign(0x1000, 0x1001) == 0x2000 bytes allocated
proc memalign*(bytes: csize, size: csize): pointer {.importc: "memalign",
header: mallocHeader.}
# Should be required, but not needed now because of how
# svcUnmapMemory frees all memory
#proc free*(address: pointer) {.importc: "free",
# header: mallocHeader.}
## Maps a memaligned block of memory from `src_addr` to `dst_addr`. The
## Nintendo Switch requires this call in order to make use of memory, otherwise
## an invalid memory access occurs.
proc svcMapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {.
importc: "svcMapMemory", header: svcHeader.}
## Unmaps (frees) all memory from both `dst_addr` and `src_addr`. **Must** be called
## whenever svcMapMemory is used. The Switch will expect all memory to be allocated
## before gfxExit() calls (<switch/gfx/gfx.h>)
proc svcUnmapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {.
importc: "svcUnmapMemory", header: svcHeader.}
proc virtmemReserveMap*(size: csize): pointer {.importc: "virtmemReserveMap",
header: virtMemHeader.}
# Should be required, but not needed now because of how
# svcUnmapMemory frees all memory
#proc virtmemFreeMap*(address: pointer; size: csize) {.importc: "virtmemFreeMap",
# header: virtMemHeader.}

View file

@ -1,6 +0,0 @@
[Package]
name = "docutils"
version = "0.9.0"
author = "Andreas Rumpf"
description = "Nimrod's reStructuredText processor."
license = "MIT"

View file

@ -0,0 +1,5 @@
name = "docutils"
version = "0.10.0"
author = "Andreas Rumpf"
description = "Nim's reStructuredText processor."
license = "MIT"

View file

@ -13,6 +13,7 @@
import
strutils
from algorithm import binarySearch
type
TokenClass* = enum
@ -129,7 +130,7 @@ proc nimNumber(g: var GeneralTokenizer, position: int): int =
const
OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
'|', '=', '%', '&', '$', '@', '~', ':'}
proc nimNextToken(g: var GeneralTokenizer) =
const
@ -365,32 +366,10 @@ proc generalStrLit(g: var GeneralTokenizer, position: int): int =
result = pos
proc isKeyword(x: openArray[string], y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
var mid = (a + b) div 2
var c = cmp(x[mid], y)
if c < 0:
a = mid + 1
elif c > 0:
b = mid - 1
else:
return mid
result = - 1
binarySearch(x, y)
proc isKeywordIgnoreCase(x: openArray[string], y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
var mid = (a + b) div 2
var c = cmpIgnoreCase(x[mid], y)
if c < 0:
a = mid + 1
elif c > 0:
b = mid - 1
else:
return mid
result = - 1
binarySearch(x, y, cmpIgnoreCase)
type
TokenizerFlag = enum
@ -902,7 +881,7 @@ when isMainModule:
break
except:
echo filename, " not found"
doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!")
doAssert(keywords.len > 0, "Couldn't read any keywords.txt file!")
for i in 0..min(keywords.len, nimKeywords.len)-1:
doAssert keywords[i] == nimKeywords[i], "Unexpected keyword"
doAssert keywords.len == nimKeywords.len, "No matching lengths"

View file

@ -43,8 +43,8 @@ type
mwUnsupportedField
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
arg: string) {.nimcall.} ## what to do in case of an error
FindFileHandler* = proc (filename: string): string {.nimcall.}
arg: string) {.closure.} ## what to do in case of an error
FindFileHandler* = proc (filename: string): string {.closure.}
const
messages: array[MsgKind, string] = [
@ -363,6 +363,7 @@ proc addNodes(n: PRstNode): string =
addNodesAux(n, result)
proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) =
if n == nil: return
if n.kind == rnLeaf:
for i in countup(0, len(n.text) - 1):
case n.text[i]
@ -853,7 +854,6 @@ type
DirKind = enum # must be ordered alphabetically!
dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
{.deprecated: [TDirKind: DirKind].}
const
DirIds: array[0..12, string] = ["", "author", "authors", "code",
@ -1114,7 +1114,6 @@ proc parseHeadline(p: var RstParser): PRstNode =
type
IntSeq = seq[int]
{.deprecated: [TIntSeq: IntSeq].}
proc tokEnd(p: RstParser): int =
result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
@ -1388,7 +1387,7 @@ proc parseSectionWrapper(p: var RstParser): PRstNode =
result = result.sons[0]
proc `$`(t: Token): string =
result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol)
result = $t.kind & ' ' & t.symbol
proc parseDoc(p: var RstParser): PRstNode =
result = parseSectionWrapper(p)
@ -1408,8 +1407,6 @@ type
hasArg, hasOptions, argIsFile, argIsWord
DirFlags = set[DirFlag]
SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags,
TSectionParser: SectionParser].}
proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
## Parses arguments and options for a directive block.

View file

@ -70,8 +70,6 @@ type
## the document or the section
level*: int ## valid for some node kinds
sons*: RstNodeSeq ## the node's sons
{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq,
TRstNode: RstNode].}
proc len*(n: PRstNode): int =
result = len(n.sons)
@ -99,7 +97,6 @@ type
RenderContext {.pure.} = object
indent: int
verbatim: int
{.deprecated: [TRenderContext: RenderContext].}
proc renderRstToRst(d: var RenderContext, n: PRstNode,
result: var string) {.gcsafe.}
@ -296,9 +293,9 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
(key: "kind", val: %($node.kind)),
(key: "level", val: %BiggestInt(node.level))
]
if node.text != nil:
if node.text.len > 0:
result.add("text", %node.text)
if node.sons != nil and len(node.sons) > 0:
if len(node.sons) > 0:
var accm = newSeq[JsonNode](len(node.sons))
for i, son in node.sons:
accm[i] = renderRstToJsonNode(son)

View file

@ -188,13 +188,16 @@ proc addTexChar(dest: var string, c: char) =
of '`': add(dest, "\\symbol{96}")
else: add(dest, c)
var splitter*: string = "<wbr />"
proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
case target
of outHtml: addXmlChar(dest, c)
of outLatex: addTexChar(dest, c)
proc addSplitter(target: OutputTarget; dest: var string) {.inline.} =
case target
of outHtml: add(dest, "<wbr />")
of outLatex: add(dest, "\\-")
proc nextSplitPoint*(s: string, start: int): int =
result = start
while result < len(s) + 0:
@ -208,15 +211,16 @@ proc nextSplitPoint*(s: string, start: int): int =
dec(result) # last valid index
proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
## Escapes the HTML.
result = ""
if splitAfter >= 0:
var partLen = 0
var j = 0
while j < len(s):
var k = nextSplitPoint(s, j)
if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
partLen = 0
add(result, splitter)
#if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
partLen = 0
addSplitter(target, result)
for i in countup(j, k): escChar(target, result, s[i])
inc(partLen, k - j + 1)
j = k + 1
@ -239,7 +243,7 @@ proc dispA(target: OutputTarget, dest: var string,
else: addf(dest, tex, args)
proc `or`(x, y: string): string {.inline.} =
result = if x.isNil: y else: x
result = if x.len == 0: y else: x
proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
@ -308,7 +312,6 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string,
## The index won't be written to disk unless you call `writeIndexFile()
## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen
## tools guide <docgen.html#index-switch>`_.
assert(not d.theIndex.isNil)
var
entry = term
isTitle = false
@ -333,7 +336,7 @@ proc hash(n: PRstNode): int =
result = hash(n.text)
elif n.len > 0:
result = hash(n.sons[0])
for i in 1 .. <len(n):
for i in 1 ..< len(n):
result = result !& hash(n.sons[i])
result = !$result
@ -384,20 +387,16 @@ proc hash(x: IndexEntry): Hash =
## Returns the hash for the combined fields of the type.
##
## The hash is computed as the chained hash of the individual string hashes.
assert(not x.keyword.isNil)
assert(not x.link.isNil)
result = x.keyword.hash !& x.link.hash
result = result !& (x.linkTitle or "").hash
result = result !& (x.linkDesc or "").hash
result = result !& x.linkTitle.hash
result = result !& x.linkDesc.hash
result = !$result
proc `<-`(a: var IndexEntry, b: IndexEntry) =
shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link
if b.linkTitle.isNil: a.linkTitle = nil
else: shallowCopy a.linkTitle, b.linkTitle
if b.linkDesc.isNil: a.linkDesc = nil
else: shallowCopy a.linkDesc, b.linkDesc
shallowCopy a.linkTitle, b.linkTitle
shallowCopy a.linkDesc, b.linkDesc
proc sortIndex(a: var openArray[IndexEntry]) =
# we use shellsort here; fast and simple
@ -441,14 +440,15 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
while j < symbols.len and keyword == symbols[j].keyword:
let
url = symbols[j].link.escapeLink
text = if not symbols[j].linkTitle.isNil: symbols[j].linkTitle else: url
desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: ""
text = if symbols[j].linkTitle.len > 0: symbols[j].linkTitle else: url
desc = if symbols[j].linkDesc.len > 0: symbols[j].linkDesc else: ""
if desc.len > 0:
result.addf("""<li><a class="reference external"
title="$3" href="$1">$2</a></li>
title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
""", [url, text, desc])
else:
result.addf("""<li><a class="reference external" href="$1">$2</a></li>
result.addf("""<li><a class="reference external"
data-doc-search-tag="$2" href="$1">$2</a></li>
""", [url, text])
inc j
result.add("</ul></dd>\n")
@ -489,6 +489,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
# Build a list of levels and extracted titles to make processing easier.
var
titleRef: string
titleTag: string
levels: seq[tuple[level: int, text: string]]
L = 0
level = 1
@ -515,14 +516,14 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
let link = entries[L].link
if link.isDocumentationTitle:
titleRef = link
titleTag = levels[L].text
else:
result.add(level.indentToLevel(levels[L].level))
result.add("<li><a href=\"" & link & "\">" &
levels[L].text & "</a></li>\n")
result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2">
$3</a></li>
""", [titleTag & " : " & levels[L].text, link, levels[L].text])
inc L
result.add(level.indentToLevel(1) & "</ul>\n")
assert(not titleRef.isNil,
"Can't use this proc on an API index, docs always have a title entry")
proc generateDocumentationIndex(docs: IndexedDocs): string =
## Returns all the documentation TOCs in an HTML hierarchical list.
@ -589,7 +590,7 @@ proc readIndexDir(dir: string):
fileEntries[F].keyword = line.substr(0, s-1)
fileEntries[F].link = line.substr(s+1)
# See if we detect a title, a link without a `#foobar` trailing part.
if title.keyword.isNil and fileEntries[F].link.isDocumentationTitle:
if title.keyword.len == 0 and fileEntries[F].link.isDocumentationTitle:
title.keyword = fileEntries[F].keyword
title.link = fileEntries[F].link
@ -600,15 +601,15 @@ proc readIndexDir(dir: string):
fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn
fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn
else:
fileEntries[F].linkTitle = nil
fileEntries[F].linkDesc = nil
fileEntries[F].linkTitle = ""
fileEntries[F].linkDesc = ""
inc F
# Depending on type add this to the list of symbols or table of APIs.
if title.keyword.isNil:
if title.keyword.len == 0:
for i in 0 .. <F:
# Don't add to symbols TOC entries (they start with a whitespace).
let toc = fileEntries[i].linkTitle
if not toc.isNil and toc.len > 0 and toc[0] == ' ':
if toc.len > 0 and toc[0] == ' ':
continue
# Ok, non TOC entry, add it.
setLen(result.symbols, L + 1)
@ -649,7 +650,6 @@ proc mergeIndexes*(dir: string): string =
## Returns the merged and sorted indices into a single HTML block which can
## be further embedded into nimdoc templates.
var (modules, symbols, docs) = readIndexDir(dir)
assert(not symbols.isNil)
result = ""
# Generate a quick jump list of documents.
@ -769,43 +769,45 @@ proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
result.add(tmp)
proc renderImage(d: PDoc, n: PRstNode, result: var string) =
template valid(s): bool =
s.len > 0 and allCharsInSet(s, {'.','/',':','%','_','\\','\128'..'\xFF'} +
Digits + Letters + WhiteSpace)
let
arg = getArgument(n)
isObject = arg.toLower().endsWith(".svg")
var
options = ""
content = ""
var s = getFieldValue(n, "scale")
if s.valid: dispA(d.target, options, if isObject: "" else: " scale=\"$1\"",
" scale=$1", [strip(s)])
s = getFieldValue(n, "height")
if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)])
var s = esc(d.target, getFieldValue(n, "scale").strip())
if s.len > 0:
dispA(d.target, options, " scale=\"$1\"", " scale=$1", [s])
s = getFieldValue(n, "width")
if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)])
s = esc(d.target, getFieldValue(n, "height").strip())
if s.len > 0:
dispA(d.target, options, " height=\"$1\"", " height=$1", [s])
s = getFieldValue(n, "alt")
if s.valid:
# <object> displays its content if it cannot render the image
if isObject: dispA(d.target, content, "$1", "", [strip(s)])
else: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)])
s = esc(d.target, getFieldValue(n, "width").strip())
if s.len > 0:
dispA(d.target, options, " width=\"$1\"", " width=$1", [s])
s = getFieldValue(n, "align")
if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
s = esc(d.target, getFieldValue(n, "alt").strip())
if s.len > 0:
dispA(d.target, options, " alt=\"$1\"", "", [s])
s = esc(d.target, getFieldValue(n, "align").strip())
if s.len > 0:
dispA(d.target, options, " align=\"$1\"", "", [s])
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
if arg.valid:
let htmlOut = if isObject:
"<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>"
else:
"<img src=\"$1\"$2 />"
dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
[arg, options])
var htmlOut = ""
if arg.endsWith(".mp4") or arg.endsWith(".ogg") or
arg.endsWith(".webm"):
htmlOut = """
<video src="$1"$2 autoPlay='true' loop='true' muted='true'>
Sorry, your browser doesn't support embedded videos
</video>
"""
else:
htmlOut = "<img src=\"$1\"$2/>"
dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
[esc(d.target, arg), options])
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
@ -820,7 +822,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
##
## This supports the special ``default-language`` internal string generated
## by the ``rst`` module to communicate a specific default language.
case n.getArgument.toLower
case n.getArgument.toLowerAscii
of "number-lines":
params.numberLines = true
# See if the field has a parameter specifying a different line than 1.
@ -836,8 +838,11 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
params.filename = n.getFieldValue.strip
of "test":
params.testCmd = n.getFieldValue.strip
if params.testCmd.len == 0: params.testCmd = "nim c -r $1"
of "status":
if params.testCmd.len == 0:
params.testCmd = "nim c -r $1"
else:
params.testCmd = unescape(params.testCmd)
of "status", "exitcode":
var status: int
if parseInt(n.getFieldValue, status) > 0:
params.status = status
@ -878,7 +883,8 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
inc d.listingCounter
let id = $d.listingCounter
if not params.numberLines:
result = (d.config.getOrDefault"doc.listing_start" % [id, $params.lang],
result = (d.config.getOrDefault"doc.listing_start" %
[id, sourceLanguageToStr[params.lang]],
d.config.getOrDefault"doc.listing_end" % id)
return
@ -891,7 +897,8 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
line.inc
codeLines.dec
result.beginTable.add("</pre></td><td>" & (
d.config.getOrDefault"doc.listing_start" % [id, $params.lang]))
d.config.getOrDefault"doc.listing_start" %
[id, sourceLanguageToStr[params.lang]]))
result.endTable = (d.config.getOrDefault"doc.listing_end" % id) &
"</td></tr></tbody></table>" & (
d.config.getOrDefault"doc.listing_button" % id)
@ -941,7 +948,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
var tmp = ""
renderRstToOut(d, n.sons[2], tmp)
var arg = strip(getArgument(n))
var arg = esc(d.target, strip(getArgument(n)))
if arg == "":
dispA(d.target, result, "<div>$1</div>", "$1", [tmp])
else:

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
{.deadCodeElim:on.}
{.deadCodeElim: on.} # dce option deprecated
from posix import SocketHandle

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
{.deadCodeElim:on.}
{.deadCodeElim: on.} # dce option deprecated
# Get the platform-dependent flags.
# Structure describing an inotify event.

View file

@ -7,8 +7,6 @@
# distribution, for details about the copyright.
#
{.deadCodeElim:on.}
from posix import Timespec
when defined(macosx) or defined(freebsd) or defined(openbsd) or
@ -61,7 +59,7 @@ const
EV_CLEAR* = 0x0020 ## Clear event state after reporting.
EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0
EV_DISPATCH* = 0x0080 ## Disable event after reporting.
EV_SYSFLAGS* = 0xF000 ## Reserved by system
EV_DROP* = 0x1000 ## Not should be dropped
EV_FLAG1* = 0x2000 ## Filter-specific flag
@ -87,10 +85,10 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly):
NOTE_FFAND* = 0x40000000'u32 ## AND fflags
NOTE_FFOR* = 0x80000000'u32 ## OR fflags
NOTE_FFCOPY* = 0xc0000000'u32 ## copy fflags
NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations
NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations
NOTE_FFLAGSMASK* = 0x00ffffff'u32
NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered
NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered
## for output.
# data/hint flags for EVFILT_{READ|WRITE}, shared with userspace

View file

@ -1,4 +1,4 @@
{.deadCodeElim:on.}
{.deadCodeElim: on.} # dce option deprecated
import posix
@ -27,4 +27,4 @@ proc clone*(fn: pointer; child_stack: pointer; flags: cint;
arg: pointer; ptid: ptr Pid; tls: pointer;
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
proc pipe2*(a: array[0..1, cint], flags: cint): cint {.importc, header: "<unistd.h>".}
proc pipe2*(a: array[0..1, cint], flags: cint): cint {.importc, header: "<unistd.h>".}

View file

@ -27,35 +27,35 @@
## resulting C code will just ``#include <XYZ.h>`` and *not* define the
## symbols declared here.
# This ensures that we don't accidentally generate #includes for files that
# might not exist on a specific platform! The user will get an error only
# if they actualy try to use the missing declaration
{.deadCodeElim: on.}
# Dead code elimination ensures that we don't accidentally generate #includes
# for files that might not exist on a specific platform! The user will get an
# error only if they actualy try to use the missing declaration
{.deadCodeElim: on.} # dce option deprecated
# TODO these constants don't seem to be fetched from a header file for unknown
# platforms - where do they come from and why are they here?
when false:
const
C_IRUSR = 0c000400 ## Read by owner.
C_IWUSR = 0c000200 ## Write by owner.
C_IXUSR = 0c000100 ## Execute by owner.
C_IRGRP = 0c000040 ## Read by group.
C_IWGRP = 0c000020 ## Write by group.
C_IXGRP = 0c000010 ## Execute by group.
C_IROTH = 0c000004 ## Read by others.
C_IWOTH = 0c000002 ## Write by others.
C_IXOTH = 0c000001 ## Execute by others.
C_ISUID = 0c004000 ## Set user ID.
C_ISGID = 0c002000 ## Set group ID.
C_ISVTX = 0c001000 ## On directories, restricted deletion flag.
C_ISDIR = 0c040000 ## Directory.
C_ISFIFO = 0c010000 ##FIFO.
C_ISREG = 0c100000 ## Regular file.
C_ISBLK = 0c060000 ## Block special.
C_ISCHR = 0c020000 ## Character special.
C_ISCTG = 0c110000 ## Reserved.
C_ISLNK = 0c120000 ## Symbolic link.</p>
C_ISSOCK = 0c140000 ## Socket.
C_IRUSR = 0o000400 ## Read by owner.
C_IWUSR = 0o000200 ## Write by owner.
C_IXUSR = 0o000100 ## Execute by owner.
C_IRGRP = 0o000040 ## Read by group.
C_IWGRP = 0o000020 ## Write by group.
C_IXGRP = 0o000010 ## Execute by group.
C_IROTH = 0o000004 ## Read by others.
C_IWOTH = 0o000002 ## Write by others.
C_IXOTH = 0o000001 ## Execute by others.
C_ISUID = 0o004000 ## Set user ID.
C_ISGID = 0o002000 ## Set group ID.
C_ISVTX = 0o001000 ## On directories, restricted deletion flag.
C_ISDIR = 0o040000 ## Directory.
C_ISFIFO = 0o010000 ##FIFO.
C_ISREG = 0o100000 ## Regular file.
C_ISBLK = 0o060000 ## Block special.
C_ISCHR = 0o020000 ## Character special.
C_ISCTG = 0o110000 ## Reserved.
C_ISLNK = 0o120000 ## Symbolic link.</p>
C_ISSOCK = 0o140000 ## Socket.
const
MM_NULLLBL* = nil
@ -82,19 +82,29 @@ const
# Special types
type Sighandler = proc (a: cint) {.noconv.}
const StatHasNanoseconds* = defined(linux) or defined(freebsd) or
defined(openbsd) or defined(dragonfly) ## \
## Boolean flag that indicates if the system supports nanosecond time
## resolution in the fields of ``Stat``. Note that the nanosecond based fields
## (``Stat.st_atim``, ``Stat.st_mtim`` and ``Stat.st_ctim``) can be accessed
## without checking this flag, because this module defines fallback procs
## when they are not available.
# Platform specific stuff
when defined(linux) and defined(amd64):
include posix_linux_amd64
elif defined(nintendoswitch):
include posix_nintendoswitch
else:
include posix_other
# There used to be this name in posix.nim a long time ago, not sure why!
{.deprecated: [cSIG_HOLD: SIG_HOLD].}
when not defined(macosx) and not defined(android):
when StatHasNanoseconds:
proc st_atime*(s: Stat): Time {.inline.} =
## Second-granularity time of last access
## Second-granularity time of last access.
result = s.st_atim.tv_sec
proc st_mtime*(s: Stat): Time {.inline.} =
## Second-granularity time of last data modification.
@ -102,6 +112,16 @@ when not defined(macosx) and not defined(android):
proc st_ctime*(s: Stat): Time {.inline.} =
## Second-granularity time of last status change.
result = s.st_ctim.tv_sec
else:
proc st_atim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last access.
result.tv_sec = s.st_atime
proc st_mtim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last data modification.
result.tv_sec = s.st_mtime
proc st_ctim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last data modification.
result.tv_sec = s.st_ctime
when hasAioH:
proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}
@ -167,7 +187,7 @@ proc fnmatch*(a1, a2: cstring, a3: cint): cint {.importc, header: "<fnmatch.h>".
proc ftw*(a1: cstring,
a2: proc (x1: cstring, x2: ptr Stat, x3: cint): cint {.noconv.},
a3: cint): cint {.importc, header: "<ftw.h>".}
when not (defined(linux) and defined(amd64)):
when not (defined(linux) and defined(amd64)) and not defined(nintendoswitch):
proc nftw*(a1: cstring,
a2: proc (x1: cstring, x2: ptr Stat,
x3: cint, x4: ptr FTW): cint {.noconv.},
@ -208,25 +228,26 @@ proc setlocale*(a1: cint, a2: cstring): cstring {.
proc strfmon*(a1: cstring, a2: int, a3: cstring): int {.varargs,
importc, header: "<monetary.h>".}
proc mq_close*(a1: Mqd): cint {.importc, header: "<mqueue.h>".}
proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}
proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {.
importc, header: "<mqueue.h>".}
proc mq_open*(a1: cstring, a2: cint): Mqd {.
varargs, importc, header: "<mqueue.h>".}
proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {.
importc, header: "<mqueue.h>".}
proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {.
importc, header: "<mqueue.h>".}
proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}
when not defined(nintendoswitch):
proc mq_close*(a1: Mqd): cint {.importc, header: "<mqueue.h>".}
proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}
proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {.
importc, header: "<mqueue.h>".}
proc mq_open*(a1: cstring, a2: cint): Mqd {.
varargs, importc, header: "<mqueue.h>".}
proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {.
importc, header: "<mqueue.h>".}
proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {.
importc, header: "<mqueue.h>".}
proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}
proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): int {.importc, header: "<mqueue.h>".}
proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): cint {.importc, header: "<mqueue.h>".}
proc mq_unlink*(a1: cstring): cint {.importc, header: "<mqueue.h>".}
proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): int {.importc, header: "<mqueue.h>".}
proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): cint {.importc, header: "<mqueue.h>".}
proc mq_unlink*(a1: cstring): cint {.importc, header: "<mqueue.h>".}
proc getpwnam*(a1: cstring): ptr Passwd {.importc, header: "<pwd.h>".}
@ -585,7 +606,7 @@ proc posix_madvise*(a1: pointer, a2: int, a3: cint): cint {.
importc, header: "<sys/mman.h>".}
proc posix_mem_offset*(a1: pointer, a2: int, a3: var Off,
a4: var int, a5: var cint): cint {.importc, header: "<sys/mman.h>".}
when not (defined(linux) and defined(amd64)):
when not (defined(linux) and defined(amd64)) and not defined(nintendoswitch):
proc posix_typed_mem_get_info*(a1: cint,
a2: var Posix_typed_mem_info): cint {.importc, header: "<sys/mman.h>".}
proc posix_typed_mem_open*(a1: cstring, a2, a3: cint): cint {.
@ -695,12 +716,12 @@ proc sigwait*(a1: var Sigset, a2: var cint): cint {.
proc sigwaitinfo*(a1: var Sigset, a2: var SigInfo): cint {.
importc, header: "<signal.h>".}
proc catclose*(a1: Nl_catd): cint {.importc, header: "<nl_types.h>".}
proc catgets*(a1: Nl_catd, a2, a3: cint, a4: cstring): cstring {.
importc, header: "<nl_types.h>".}
proc catopen*(a1: cstring, a2: cint): Nl_catd {.
importc, header: "<nl_types.h>".}
when not defined(nintendoswitch):
proc catclose*(a1: Nl_catd): cint {.importc, header: "<nl_types.h>".}
proc catgets*(a1: Nl_catd, a2, a3: cint, a4: cstring): cstring {.
importc, header: "<nl_types.h>".}
proc catopen*(a1: cstring, a2: cint): Nl_catd {.
importc, header: "<nl_types.h>".}
proc sched_get_priority_max*(a1: cint): cint {.importc, header: "<sched.h>".}
proc sched_get_priority_min*(a1: cint): cint {.importc, header: "<sched.h>".}
@ -782,11 +803,12 @@ when hasSpawnH:
a4: var Tposix_spawnattr,
a5, a6: cstringArray): cint {.importc, header: "<spawn.h>".}
proc getcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc makecontext*(a1: var Ucontext, a4: proc (){.noconv.}, a3: cint) {.
varargs, importc, header: "<ucontext.h>".}
proc setcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc swapcontext*(a1, a2: var Ucontext): cint {.importc, header: "<ucontext.h>".}
when not defined(nintendoswitch):
proc getcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc makecontext*(a1: var Ucontext, a4: proc (){.noconv.}, a3: cint) {.
varargs, importc, header: "<ucontext.h>".}
proc setcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc swapcontext*(a1, a2: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc readv*(a1: cint, a2: ptr IOVec, a3: cint): int {.
importc, header: "<sys/uio.h>".}
@ -973,3 +995,19 @@ template onSignal*(signals: varargs[cint], body: untyped) =
proc (sig: cint) {.noconv.} =
body
)
type
RLimit* {.importc: "struct rlimit",
header: "<sys/resource.h>", pure, final.} = object
rlim_cur*: int
rlim_max*: int
## The getrlimit() and setrlimit() system calls get and set resource limits respectively.
## Each resource has an associated soft and hard limit, as defined by the RLimit structure
proc setrlimit*(resource: cint, rlp: var RLimit): cint
{.importc: "setrlimit",header: "<sys/resource.h>".}
## The setrlimit() system calls sets resource limits.
proc getrlimit*(resource: cint, rlp: var RLimit): cint
{.importc: "getrlimit",header: "<sys/resource.h>".}
## The getrlimit() system call gets resource limits.

View file

@ -351,8 +351,8 @@ type
Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec*: clong ## Seconds.
tv_usec*: clong ## Microseconds.
tv_sec*: Time ## Seconds.
tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object
abi: array[1024 div (8 * sizeof(clong)), clong]

View file

@ -300,7 +300,7 @@ const IPPROTO_TCP* = cint(6)
const IPPROTO_UDP* = cint(17)
const INADDR_ANY* = InAddrScalar(0)
const INADDR_LOOPBACK* = InAddrScalar(2130706433)
const INADDR_BROADCAST* = InAddrScalar(-1)
const INADDR_BROADCAST* = InAddrScalar(4294967295)
const INET_ADDRSTRLEN* = cint(16)
const INET6_ADDRSTRLEN* = cint(46)
const IPV6_JOIN_GROUP* = cint(20)
@ -433,6 +433,9 @@ const POSIX_MADV_WILLNEED* = cint(3)
const POSIX_MADV_DONTNEED* = cint(4)
const MAP_POPULATE* = cint(32768)
# <sys/resource.h>
const RLIMIT_NOFILE* = cint(7)
# <sys/select.h>
const FD_SETSIZE* = cint(1024)

View file

@ -0,0 +1,506 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2018 Joey Yakimowich-Payne
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# To be included from posix.nim!
const
hasSpawnH = true
hasAioH = false
type
DIR* {.importc: "DIR", header: "<dirent.h>",
incompleteStruct.} = object
const SIG_HOLD* = cast[SigHandler](2)
type
SocketHandle* = distinct cint # The type used to represent socket descriptors
type
Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
Timespec* {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object ## struct timespec
tv_sec*: Time ## Seconds.
tv_nsec*: clong ## Nanoseconds.
Dirent* {.importc: "struct dirent",
header: "<dirent.h>", final, pure.} = object ## dirent_t struct
d_ino*: Ino
d_type*: int8 # cuchar really!
d_name*: array[256, cchar]
Tflock* {.importc: "struct flock", final, pure,
header: "<fcntl.h>".} = object ## flock type
l_type*: cshort ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
l_whence*: cshort ## Flag for starting offset.
l_start*: Off ## Relative offset in bytes.
l_len*: Off ## Size; if 0 then until EOF.
l_pid*: Pid ## Process ID of the process holding the lock;
## returned with F_GETLK.
# no struct FTW on linux
Glob* {.importc: "glob_t", header: "<glob.h>",
final, pure.} = object ## glob_t
gl_pathc*: cint ## Count of paths matched by pattern.
gl_matchc*: cint ## Count of paths matching pattern
gl_offs*: cint ## Slots to reserve at the beginning of gl_pathv.
gl_flags*: cint
gl_pathv*: cstringArray ## Pointer to a list of matched pathnames.
gl_errfunc*: pointer
gl_closedir*: pointer
gl_readdir*: pointer
gl_opendir*: pointer
gl_lstat*: pointer
gl_stat*: pointer
Group* {.importc: "struct group", header: "<grp.h>",
final, pure.} = object ## struct group
gr_name*: cstring ## The name of the group.
gr_passwd*: cstring
gr_gid*: Gid ## Numerical group ID.
gr_mem*: cstringArray ## Pointer to a null-terminated array of character
## pointers to member names.
Iconv* {.importc: "iconv_t", header: "<iconv.h>".} = pointer
## Identifies the conversion from one codeset to another.
Lconv* {.importc: "struct lconv", header: "<locale.h>", final,
pure.} = object
decimal_point*: cstring
thousands_sep*: cstring
grouping*: cstring
int_curr_symbol*: cstring
currency_symbol*: cstring
mon_decimal_point*: cstring
mon_thousands_sep*: cstring
mon_grouping*: cstring
positive_sign*: cstring
negative_sign*: cstring
int_frac_digits*: char
frac_digits*: char
p_cs_precedes*: char
p_sep_by_space*: char
n_cs_precedes*: char
n_sep_by_space*: char
p_sign_posn*: char
n_sign_posn*: char
int_n_cs_precedes*: char
int_n_sep_by_space*: char
int_n_sign_posn*: char
int_p_cs_precedes*: char
int_p_sep_by_space*: char
int_p_sign_posn*: char
Passwd* {.importc: "struct passwd", header: "<pwd.h>",
final, pure.} = object ## struct passwd
pw_name*: cstring ## User's login name.
pw_passwd*: cstring
pw_uid*: Uid ## Numerical user ID.
pw_gid*: Gid ## Numerical group ID.
pw_comment*: cstring
pw_gecos*: cstring
pw_dir*: cstring ## Initial working directory.
pw_shell*: cstring ## Program to use as shell.
Blkcnt* {.importc: "blkcnt_t", header: "<sys/types.h>".} = clong
## used for file block counts
Blksize* {.importc: "blksize_t", header: "<sys/types.h>".} = clong
## used for block sizes
Clock* {.importc: "clock_t", header: "<sys/types.h>".} = clong
ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = cint
Dev* {.importc: "dev_t", header: "<sys/types.h>".} = culong
Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = culong
Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = culong
Gid* {.importc: "gid_t", header: "<sys/types.h>".} = cuint
Id* {.importc: "id_t", header: "<sys/types.h>".} = cuint
Ino* {.importc: "ino_t", header: "<sys/types.h>".} = culong
Key* {.importc: "key_t", header: "<sys/types.h>".} = cint
Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint # cuint really!
Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = culong
Off* {.importc: "off_t", header: "<sys/types.h>".} = clong
Pid* {.importc: "pid_t", header: "<sys/types.h>".} = cint
Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>",
pure, final.} = object
abi: array[56 div sizeof(clong), clong]
Pthread_barrier* {.importc: "pthread_barrier_t",
header: "<sys/types.h>", pure, final.} = object
abi: array[32 div sizeof(clong), clong]
Pthread_barrierattr* {.importc: "pthread_barrierattr_t",
header: "<sys/types.h>", pure, final.} = object
abi: array[4 div sizeof(cint), cint]
Pthread_cond* {.importc: "pthread_cond_t", header: "<sys/types.h>",
pure, final.} = object
abi: array[48 div sizeof(clonglong), clonglong]
Pthread_condattr* {.importc: "pthread_condattr_t",
header: "<sys/types.h>", pure, final.} = object
abi: array[4 div sizeof(cint), cint]
Pthread_key* {.importc: "pthread_key_t", header: "<sys/types.h>".} = cuint
Pthread_mutex* {.importc: "pthread_mutex_t", header: "<sys/types.h>",
pure, final.} = object
abi: array[48 div sizeof(clong), clong]
Pthread_mutexattr* {.importc: "pthread_mutexattr_t",
header: "<sys/types.h>", pure, final.} = object
abi: array[4 div sizeof(cint), cint]
Pthread_once* {.importc: "pthread_once_t", header: "<sys/types.h>".} = cint
Pthread_rwlock* {.importc: "pthread_rwlock_t",
header: "<sys/types.h>", pure, final.} = object
abi: array[56 div sizeof(clong), clong]
Pthread_rwlockattr* {.importc: "pthread_rwlockattr_t",
header: "<sys/types.h>".} = object
abi: array[8 div sizeof(clong), clong]
Pthread_spinlock* {.importc: "pthread_spinlock_t",
header: "<sys/types.h>".} = cint
Pthread* {.importc: "pthread_t", header: "<sys/types.h>".} = culong
Suseconds* {.importc: "suseconds_t", header: "<sys/types.h>".} = clong
#Ttime* {.importc: "time_t", header: "<sys/types.h>".} = int
Timer* {.importc: "timer_t", header: "<sys/types.h>".} = pointer
Uid* {.importc: "uid_t", header: "<sys/types.h>".} = cuint
Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = cuint
Utsname* {.importc: "struct utsname",
header: "<sys/utsname.h>",
final, pure.} = object ## struct utsname
sysname*, ## Name of this implementation of the operating system.
nodename*, ## Name of this node within the communications
## network to which this node is attached, if any.
release*, ## Current release level of this implementation.
version*, ## Current version level of this release.
machine*, ## Name of the hardware type on which the
## system is running.
domainname*: array[65, char]
Sem* {.importc: "sem_t", header: "<semaphore.h>", final, pure.} = object
abi: array[32 div sizeof(clong), clong]
Stat* {.importc: "struct stat",
header: "<sys/stat.h>", final, pure.} = object ## struct stat
st_dev*: Dev ## Device ID of device containing file.
st_ino*: Ino ## File serial number.
st_mode*: Mode ## Mode of file (see below).
st_nlink*: Nlink ## Number of hard links to the file.
st_uid*: Uid ## User ID of file.
st_gid*: Gid ## Group ID of file.
st_rdev*: Dev ## Device ID (if file is character or block special).
st_size*: Off ## For regular files, the file size in bytes.
## For symbolic links, the length in bytes of the
## pathname contained in the symbolic link.
## For a shared memory object, the length in bytes.
## For a typed memory object, the length in bytes.
## For other file types, the use of this field is
## unspecified.
when StatHasNanoseconds:
st_atim*: Timespec ## Time of last access.
pad1: clong
st_mtim*: Timespec ## Time of last data modification.
pad2: clong
st_ctim*: Timespec ## Time of last status change.
pad3: clong
else:
st_atime*: Time ## Time of last access.
pad1: clong
st_mtime*: Time ## Time of last data modification.
pad2: clong
st_ctime*: Time ## Time of last status change.
pad3: clong
st_blksize*: Blksize ## A file system-specific preferred I/O block size
## for this object. In some file system types, this
## may vary from file to file.
st_blocks*: Blkcnt ## Number of blocks allocated for this object.
reserved: array[2, clong]
Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
final, pure.} = object ## struct statvfs
f_bsize*: culong ## File system block size.
f_frsize*: culong ## Fundamental file system block size.
f_blocks*: Fsblkcnt ## Total number of blocks on file system
## in units of f_frsize.
f_bfree*: Fsblkcnt ## Total number of free blocks.
f_bavail*: Fsblkcnt ## Number of free blocks available to
## non-privileged process.
f_files*: Fsfilcnt ## Total number of file serial numbers.
f_ffree*: Fsfilcnt ## Total number of free file serial numbers.
f_favail*: Fsfilcnt ## Number of file serial numbers available to
## non-privileged process.
f_fsid*: culong ## File system ID.
f_flag*: culong ## Bit mask of f_flag values.
f_namemax*: culong ## Maximum filename length.
# No Posix_typed_mem_info
Tm* {.importc: "struct tm", header: "<time.h>",
final, pure.} = object ## struct tm
tm_sec*: cint ## Seconds [0,60].
tm_min*: cint ## Minutes [0,59].
tm_hour*: cint ## Hour [0,23].
tm_mday*: cint ## Day of month [1,31].
tm_mon*: cint ## Month of year [0,11].
tm_year*: cint ## Years since 1900.
tm_wday*: cint ## Day of week [0,6] (Sunday =0).
tm_yday*: cint ## Day of year [0,365].
tm_isdst*: cint ## Daylight Savings flag.
Itimerspec* {.importc: "struct itimerspec", header: "<time.h>",
final, pure.} = object ## struct itimerspec
it_interval*: Timespec ## Timer period.
it_value*: Timespec ## Timer expiration.
Sig_atomic* {.importc: "sig_atomic_t", header: "<signal.h>".} = cint
## Possibly volatile-qualified integer type of an object that can be
## accessed as an atomic entity, even in the presence of asynchronous
## interrupts.
Sigset* {.importc: "sigset_t", header: "<signal.h>", final.} = culong
SigEvent* {.importc: "struct sigevent",
header: "<signal.h>", final, pure.} = object ## struct sigevent
sigev_notify*: cint ## Notification type.
sigev_signo*: cint ## Signal number.
sigev_value*: SigVal ## Signal value.
SigVal* {.importc: "union sigval",
header: "<signal.h>", final, pure.} = object ## struct sigval
sival_int*: cint ## integer signal value
sival_ptr*: pointer ## pointer signal value;
Sigaction* {.importc: "struct sigaction",
header: "<signal.h>", final, pure.} = object ## struct sigaction
sa_handler*: proc (x: cint) {.noconv.} ## Pointer to a signal-catching
## function or one of the macros
## SIG_IGN or SIG_DFL.
sa_mask*: Sigset ## Set of signals to be blocked during execution of
## the signal handling function.
sa_flags*: cint ## Special flags.
Stack* {.importc: "stack_t",
header: "<signal.h>", final, pure.} = object ## stack_t
ss_sp*: pointer ## Stack base or pointer.
ss_flags*: cint ## Flags.
ss_size*: csize ## Stack size.
SigInfo* {.importc: "siginfo_t",
header: "<signal.h>", final, pure.} = object ## siginfo_t
si_signo*: cint ## Signal number.
si_code*: cint ## Signal code.
si_value*: SigVal ## Signal value.
Nl_item* {.importc: "nl_item", header: "<langinfo.h>".} = cint
Sched_param* {.importc: "struct sched_param",
header: "<sched.h>",
final, pure.} = object ## struct sched_param
sched_priority*: cint
Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec*: Time ## Seconds.
tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object
abi: array[((64+(sizeof(clong) * 8)-1) div (sizeof(clong) * 8)), clong]
proc si_pid*(info: SigInfo): Pid =
## This might not be correct behavior. si_pid doesn't exist in Switch's
## devkitpro headers
raise newException(OSError, "Nintendo switch cannot get si_pid!")
type
Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
final, pure.} = object ## struct aiocb
aio_fildes*: cint ## File descriptor.
aio_lio_opcode*: cint ## Operation to be performed.
aio_reqprio*: cint ## Request priority offset.
aio_buf*: pointer ## Location of buffer.
aio_nbytes*: csize ## Length of transfer.
aio_sigevent*: SigEvent ## Signal number and value.
next_prio: pointer
abs_prio: cint
policy: cint
error_Code: cint
return_value: clong
aio_offset*: Off ## File offset.
reserved: array[32, uint8]
type
Tposix_spawnattr* {.importc: "posix_spawnattr_t",
header: "<spawn.h>", final, pure.} = object
Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t",
header: "<spawn.h>", final, pure.} = object
# from sys/un.h
const Sockaddr_un_path_length* = 108
type
Socklen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
# cushort really
TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = cshort
SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
pure, final.} = object ## struct sockaddr
sa_len: cuchar
sa_family*: TSa_Family ## Address family.
sa_data*: array[14, char] ## Socket address (variable-length data).
Sockaddr_storage* {.importc: "struct sockaddr_storage",
header: "<sys/socket.h>",
pure, final.} = object ## struct sockaddr_storage
ss_len: cuchar
ss_family*: TSa_Family ## Address family.
ss_padding1: array[64 - sizeof(cuchar) - sizeof(cshort), char]
ss_align: clonglong
ss_padding2: array[
128 - sizeof(cuchar) - sizeof(cshort) -
(64 - sizeof(cuchar) - sizeof(cshort)) - 64, char]
Tif_nameindex* {.importc: "struct if_nameindex", final,
pure, header: "<net/if.h>".} = object ## struct if_nameindex
if_index*: cuint ## Numeric index of the interface.
if_name*: cstring ## Null-terminated name of the interface.
IOVec* {.importc: "struct iovec", pure, final,
header: "<sys/socket.h>".} = object ## struct iovec
iov_base*: pointer ## Base address of a memory region for input or output.
iov_len*: csize ## The size of the memory pointed to by iov_base.
Tmsghdr* {.importc: "struct msghdr", pure, final,
header: "<sys/socket.h>".} = object ## struct msghdr
msg_name*: pointer ## Optional address.
msg_namelen*: Socklen ## Size of address.
msg_iov*: ptr IOVec ## Scatter/gather array.
msg_iovlen*: csize ## Members in msg_iov.
msg_control*: pointer ## Ancillary data; see below.
msg_controllen*: csize ## Ancillary data buffer len.
msg_flags*: cint ## Flags on received message.
Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
header: "<sys/socket.h>".} = object ## struct cmsghdr
cmsg_len*: csize ## Data byte count, including the cmsghdr.
cmsg_level*: cint ## Originating protocol.
cmsg_type*: cint ## Protocol-specific type.
TLinger* {.importc: "struct linger", pure, final,
header: "<sys/socket.h>".} = object ## struct linger
l_onoff*: cint ## Indicates whether linger option is enabled.
l_linger*: cint ## Linger time, in seconds.
# data follows...
InPort* = uint16
InAddrScalar* = uint32
InAddrT* {.importc: "in_addr_t", pure, final,
header: "<netinet/in.h>".} = uint32
InAddr* {.importc: "struct in_addr", pure, final,
header: "<netinet/in.h>".} = object ## struct in_addr
s_addr*: InAddrScalar
Sockaddr_in* {.importc: "struct sockaddr_in", pure, final,
header: "<netinet/in.h>".} = object ## struct sockaddr_in
sin_len*: cushort
sin_family*: TSa_Family ## AF_INET.
sin_port*: InPort ## Port number.
sin_addr*: InAddr ## IP address.
sin_zero: array[8, uint8]
In6Addr* {.importc: "struct in6_addr", pure, final,
header: "<netinet/in.h>".} = object ## struct in6_addr
s6_addr*: array[0..15, char]
Sockaddr_in6* {.importc: "struct sockaddr_in6", pure, final,
header: "<netinet/in.h>".} = object ## struct sockaddr_in6
sin6_family*: TSa_Family ## AF_INET6.
sin6_port*: InPort ## Port number.
sin6_flowinfo*: uint32 ## IPv6 traffic class and flow information.
sin6_addr*: In6Addr ## IPv6 address.
sin6_scope_id*: uint32 ## Set of interfaces for a scope.
Hostent* {.importc: "struct hostent", pure, final,
header: "<netdb.h>".} = object ## struct hostent
h_name*: cstring ## Official name of the host.
h_aliases*: cstringArray ## A pointer to an array of pointers to
## alternative host names, terminated by a
## null pointer.
h_addrtype*: cint ## Address type.
h_length*: cint ## The length, in bytes, of the address.
h_addr_list*: cstringArray ## A pointer to an array of pointers to network
## addresses (in network byte order) for the
## host, terminated by a null pointer.
Tnetent* {.importc: "struct netent", pure, final,
header: "<netdb.h>".} = object ## struct netent
n_name*: cstring ## Official, fully-qualified (including the
## domain) name of the host.
n_aliases*: cstringArray ## A pointer to an array of pointers to
## alternative network names, terminated by a
## null pointer.
n_addrtype*: cint ## The address type of the network.
n_net*: uint32 ## The network number, in host byte order.
Protoent* {.importc: "struct protoent", pure, final,
header: "<netdb.h>".} = object ## struct protoent
p_name*: cstring ## Official name of the protocol.
p_aliases*: cstringArray ## A pointer to an array of pointers to
## alternative protocol names, terminated by
## a null pointer.
p_proto*: cint ## The protocol number.
Servent* {.importc: "struct servent", pure, final,
header: "<netdb.h>".} = object ## struct servent
s_name*: cstring ## Official name of the service.
s_aliases*: cstringArray ## A pointer to an array of pointers to
## alternative service names, terminated by
## a null pointer.
s_port*: cint ## The port number at which the service
## resides, in network byte order.
s_proto*: cstring ## The name of the protocol to use when
## contacting the service.
AddrInfo* {.importc: "struct addrinfo", pure, final,
header: "<netdb.h>".} = object ## struct addrinfo
ai_flags*: cint ## Input flags.
ai_family*: cint ## Address family of socket.
ai_socktype*: cint ## Socket type.
ai_protocol*: cint ## Protocol of socket.
ai_addrlen*: Socklen ## Length of socket address.
ai_canonname*: cstring ## Canonical name of service location.
ai_addr*: ptr SockAddr ## Socket address of socket.
ai_next*: ptr AddrInfo ## Pointer to next in list.
TPollfd* {.importc: "struct pollfd", pure, final,
header: "<poll.h>".} = object ## struct pollfd
fd*: cint ## The following descriptor being polled.
events*: cshort ## The input event flags (see below).
revents*: cshort ## The output event flags (see below).
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
var
errno* {.importc, header: "<errno.h>".}: cint ## error variable
h_errno* {.importc, header: "<netdb.h>".}: cint
daylight* {.importc: "_daylight", header: "<time.h>".}: cint
timezone* {.importc: "_timezone", header: "<time.h>".}: clong
# Regenerate using detect.nim!
include posix_nintendoswitch_consts
const POSIX_SPAWN_USEVFORK* = cint(0x40) # needs _GNU_SOURCE!
# <sys/wait.h>
proc WEXITSTATUS*(s: cint): cint = (s shr 8) and 0xff
proc WIFEXITED*(s:cint) : bool = (s and 0xff) == 0
proc WTERMSIG*(s:cint): cint = s and 0x7f
proc WSTOPSIG*(s:cint): cint = WEXITSTATUS(s)
proc WIFSIGNALED*(s:cint) : bool = ((s and 0x7f) > 0) and ((s and 0x7f) < 0x7f)
proc WIFSTOPPED*(s:cint) : bool = (s and 0xff) == 0x7f

View file

@ -0,0 +1,586 @@
# Generated by detect.nim
# <aio.h>
# <dlfcn.h>
# <errno.h>
const E2BIG* = cint(7)
const EACCES* = cint(13)
const EADDRINUSE* = cint(112)
const EADDRNOTAVAIL* = cint(125)
const EAFNOSUPPORT* = cint(106)
const EAGAIN* = cint(11)
const EALREADY* = cint(120)
const EBADF* = cint(9)
const EBADMSG* = cint(77)
const EBUSY* = cint(16)
const ECANCELED* = cint(140)
const ECHILD* = cint(10)
const ECONNABORTED* = cint(113)
const ECONNREFUSED* = cint(111)
const ECONNRESET* = cint(104)
const EDEADLK* = cint(45)
const EDESTADDRREQ* = cint(121)
const EDOM* = cint(33)
const EDQUOT* = cint(132)
const EEXIST* = cint(17)
const EFAULT* = cint(14)
const EFBIG* = cint(27)
const EHOSTUNREACH* = cint(118)
const EIDRM* = cint(36)
const EILSEQ* = cint(138)
const EINPROGRESS* = cint(119)
const EINTR* = cint(4)
const EINVAL* = cint(22)
const EIO* = cint(5)
const EISCONN* = cint(127)
const EISDIR* = cint(21)
const ELOOP* = cint(92)
const EMFILE* = cint(24)
const EMLINK* = cint(31)
const EMSGSIZE* = cint(122)
const EMULTIHOP* = cint(74)
const ENAMETOOLONG* = cint(91)
const ENETDOWN* = cint(115)
const ENETRESET* = cint(126)
const ENETUNREACH* = cint(114)
const ENFILE* = cint(23)
const ENOBUFS* = cint(105)
const ENODATA* = cint(61)
const ENODEV* = cint(19)
const ENOENT* = cint(2)
const ENOEXEC* = cint(8)
const ENOLCK* = cint(46)
const ENOLINK* = cint(67)
const ENOMEM* = cint(12)
const ENOMSG* = cint(35)
const ENOPROTOOPT* = cint(109)
const ENOSPC* = cint(28)
const ENOSR* = cint(63)
const ENOSTR* = cint(60)
const ENOSYS* = cint(88)
const ENOTCONN* = cint(128)
const ENOTDIR* = cint(20)
const ENOTEMPTY* = cint(90)
const ENOTSOCK* = cint(108)
const ENOTSUP* = cint(134)
const ENOTTY* = cint(25)
const ENXIO* = cint(6)
const EOPNOTSUPP* = cint(95)
const EOVERFLOW* = cint(139)
const EPERM* = cint(1)
const EPIPE* = cint(32)
const EPROTO* = cint(71)
const EPROTONOSUPPORT* = cint(123)
const EPROTOTYPE* = cint(107)
const ERANGE* = cint(34)
const EROFS* = cint(30)
const ESPIPE* = cint(29)
const ESRCH* = cint(3)
const ESTALE* = cint(133)
const ETIME* = cint(62)
const ETIMEDOUT* = cint(116)
const ETXTBSY* = cint(26)
const EWOULDBLOCK* = cint(11)
const EXDEV* = cint(18)
# <fcntl.h>
const F_DUPFD* = cint(0)
const F_GETFD* = cint(1)
const F_SETFD* = cint(2)
const F_GETFL* = cint(3)
const F_SETFL* = cint(4)
const F_GETLK* = cint(7)
const F_SETLK* = cint(8)
const F_SETLKW* = cint(9)
const F_GETOWN* = cint(5)
const F_SETOWN* = cint(6)
const FD_CLOEXEC* = cint(1)
const F_RDLCK* = cint(1)
const F_UNLCK* = cint(3)
const F_WRLCK* = cint(2)
const O_CREAT* = cint(512)
const O_EXCL* = cint(2048)
const O_NOCTTY* = cint(32768)
const O_TRUNC* = cint(1024)
const O_APPEND* = cint(8)
const O_NONBLOCK* = cint(16384)
const O_SYNC* = cint(8192)
const O_ACCMODE* = cint(3)
const O_RDONLY* = cint(0)
const O_RDWR* = cint(2)
const O_WRONLY* = cint(1)
# <fenv.h>
# <fmtmsg.h>
# <fnmatch.h>
const FNM_NOMATCH* = cint(1)
const FNM_PATHNAME* = cint(2)
const FNM_PERIOD* = cint(4)
const FNM_NOESCAPE* = cint(1)
# <ftw.h>
# <glob.h>
const GLOB_APPEND* = cint(1)
const GLOB_DOOFFS* = cint(2)
const GLOB_ERR* = cint(4)
const GLOB_MARK* = cint(8)
const GLOB_NOCHECK* = cint(16)
const GLOB_NOSORT* = cint(32)
const GLOB_NOSPACE* = cint(-1)
# <langinfo.h>
const CODESET* = cint(0)
const D_T_FMT* = cint(1)
const D_FMT* = cint(2)
const T_FMT* = cint(3)
const T_FMT_AMPM* = cint(4)
const AM_STR* = cint(5)
const PM_STR* = cint(6)
const DAY_1* = cint(7)
const DAY_2* = cint(8)
const DAY_3* = cint(9)
const DAY_4* = cint(10)
const DAY_5* = cint(11)
const DAY_6* = cint(12)
const DAY_7* = cint(13)
const ABDAY_1* = cint(14)
const ABDAY_2* = cint(15)
const ABDAY_3* = cint(16)
const ABDAY_4* = cint(17)
const ABDAY_5* = cint(18)
const ABDAY_6* = cint(19)
const ABDAY_7* = cint(20)
const MON_1* = cint(21)
const MON_2* = cint(22)
const MON_3* = cint(23)
const MON_4* = cint(24)
const MON_5* = cint(25)
const MON_6* = cint(26)
const MON_7* = cint(27)
const MON_8* = cint(28)
const MON_9* = cint(29)
const MON_10* = cint(30)
const MON_11* = cint(31)
const MON_12* = cint(32)
const ABMON_1* = cint(33)
const ABMON_2* = cint(34)
const ABMON_3* = cint(35)
const ABMON_4* = cint(36)
const ABMON_5* = cint(37)
const ABMON_6* = cint(38)
const ABMON_7* = cint(39)
const ABMON_8* = cint(40)
const ABMON_9* = cint(41)
const ABMON_10* = cint(42)
const ABMON_11* = cint(43)
const ABMON_12* = cint(44)
const ERA* = cint(45)
const ERA_D_FMT* = cint(46)
const ERA_D_T_FMT* = cint(47)
const ERA_T_FMT* = cint(48)
const ALT_DIGITS* = cint(49)
const RADIXCHAR* = cint(50)
const THOUSEP* = cint(51)
const YESEXPR* = cint(52)
const NOEXPR* = cint(53)
const CRNCYSTR* = cint(56)
# <locale.h>
const LC_ALL* = cint(0)
const LC_COLLATE* = cint(1)
const LC_CTYPE* = cint(2)
const LC_MESSAGES* = cint(6)
const LC_MONETARY* = cint(3)
const LC_NUMERIC* = cint(4)
const LC_TIME* = cint(5)
# <netdb.h>
const IPPORT_RESERVED* = cint(1024)
const HOST_NOT_FOUND* = cint(1)
const NO_DATA* = cint(4)
const NO_RECOVERY* = cint(3)
const TRY_AGAIN* = cint(2)
const AI_PASSIVE* = cint(1)
const AI_CANONNAME* = cint(2)
const AI_NUMERICHOST* = cint(4)
const AI_NUMERICSERV* = cint(8)
const AI_V4MAPPED* = cint(2048)
const AI_ALL* = cint(256)
const AI_ADDRCONFIG* = cint(1024)
const NI_NOFQDN* = cint(1)
const NI_NUMERICHOST* = cint(2)
const NI_NAMEREQD* = cint(4)
const NI_NUMERICSERV* = cint(8)
const NI_NUMERICSCOPE* = cint(32)
const NI_DGRAM* = cint(16)
const EAI_AGAIN* = cint(2)
const EAI_BADFLAGS* = cint(3)
const EAI_FAIL* = cint(4)
const EAI_FAMILY* = cint(5)
const EAI_MEMORY* = cint(6)
const EAI_NONAME* = cint(8)
const EAI_SERVICE* = cint(9)
const EAI_SOCKTYPE* = cint(10)
const EAI_SYSTEM* = cint(11)
const EAI_OVERFLOW* = cint(14)
# <net/if.h>
const IF_NAMESIZE* = cint(16)
# <netinet/in.h>
const IPPROTO_IP* = cint(0)
const IPPROTO_IPV6* = cint(41)
const IPPROTO_ICMP* = cint(1)
const IPPROTO_RAW* = cint(255)
const IPPROTO_TCP* = cint(6)
const IPPROTO_UDP* = cint(17)
const INADDR_ANY* = InAddrScalar(0)
const INADDR_LOOPBACK* = InAddrScalar(2130706433)
const INADDR_BROADCAST* = InAddrScalar(-1)
const INET_ADDRSTRLEN* = cint(16)
const INET6_ADDRSTRLEN* = cint(46)
const IPV6_JOIN_GROUP* = cint(12)
const IPV6_LEAVE_GROUP* = cint(13)
const IPV6_MULTICAST_HOPS* = cint(10)
const IPV6_MULTICAST_IF* = cint(9)
const IPV6_MULTICAST_LOOP* = cint(11)
const IPV6_UNICAST_HOPS* = cint(4)
const IPV6_V6ONLY* = cint(27)
# <netinet/tcp.h>
const TCP_NODELAY* = cint(1)
# <nl_types.h>
# <poll.h>
const POLLIN* = cshort(1)
const POLLRDNORM* = cshort(64)
const POLLRDBAND* = cshort(128)
const POLLPRI* = cshort(2)
const POLLOUT* = cshort(4)
const POLLWRNORM* = cshort(4)
const POLLWRBAND* = cshort(256)
const POLLERR* = cshort(8)
const POLLHUP* = cshort(16)
const POLLNVAL* = cshort(32)
# <pthread.h>
const PTHREAD_CREATE_DETACHED* = cint(0)
const PTHREAD_CREATE_JOINABLE* = cint(1)
const PTHREAD_EXPLICIT_SCHED* = cint(2)
const PTHREAD_INHERIT_SCHED* = cint(1)
const PTHREAD_SCOPE_PROCESS* = cint(0)
const PTHREAD_SCOPE_SYSTEM* = cint(1)
# <sched.h>
const SCHED_FIFO* = cint(1)
const SCHED_RR* = cint(2)
const SCHED_OTHER* = cint(0)
# <semaphore.h>
# <signal.h>
const SIGEV_NONE* = cint(1)
const SIGEV_SIGNAL* = cint(2)
const SIGEV_THREAD* = cint(3)
const SIGABRT* = cint(6)
const SIGALRM* = cint(14)
const SIGBUS* = cint(10)
const SIGCHLD* = cint(20)
const SIGCONT* = cint(19)
const SIGFPE* = cint(8)
const SIGHUP* = cint(1)
const SIGILL* = cint(4)
const SIGINT* = cint(2)
const SIGKILL* = cint(9)
const SIGPIPE* = cint(13)
const SIGQUIT* = cint(3)
const SIGSEGV* = cint(11)
const SIGSTOP* = cint(17)
const SIGTERM* = cint(15)
const SIGTSTP* = cint(18)
const SIGTTIN* = cint(21)
const SIGTTOU* = cint(22)
const SIGUSR1* = cint(30)
const SIGUSR2* = cint(31)
const SIGPOLL* = cint(23)
const SIGPROF* = cint(27)
const SIGSYS* = cint(12)
const SIGTRAP* = cint(5)
const SIGURG* = cint(16)
const SIGVTALRM* = cint(26)
const SIGXCPU* = cint(24)
const SIGXFSZ* = cint(25)
const SA_NOCLDSTOP* = cint(1)
const SIG_BLOCK* = cint(1)
const SIG_UNBLOCK* = cint(2)
const SIG_SETMASK* = cint(0)
const SS_ONSTACK* = cint(1)
const SS_DISABLE* = cint(2)
const MINSIGSTKSZ* = cint(2048)
const SIGSTKSZ* = cint(8192)
const SIG_DFL* = cast[Sighandler](0)
const SIG_ERR* = cast[Sighandler](-1)
const SIG_IGN* = cast[Sighandler](1)
# <sys/ipc.h>
# <sys/mman.h>
# <sys/resource.h>
# <sys/select.h>
const FD_SETSIZE* = cint(64)
# <sys/socket.h>
const MSG_CTRUNC* = cint(32)
const MSG_DONTROUTE* = cint(4)
const MSG_EOR* = cint(8)
const MSG_OOB* = cint(1)
const SCM_RIGHTS* = cint(1)
const SO_ACCEPTCONN* = cint(2)
const SO_BROADCAST* = cint(32)
const SO_DEBUG* = cint(1)
const SO_DONTROUTE* = cint(16)
const SO_ERROR* = cint(4103)
const SO_KEEPALIVE* = cint(8)
const SO_LINGER* = cint(128)
const SO_OOBINLINE* = cint(256)
const SO_RCVBUF* = cint(4098)
const SO_RCVLOWAT* = cint(4100)
const SO_RCVTIMEO* = cint(4102)
const SO_REUSEADDR* = cint(4)
const SO_SNDBUF* = cint(4097)
const SO_SNDLOWAT* = cint(4099)
const SO_SNDTIMEO* = cint(4101)
const SO_TYPE* = cint(4104)
const SOCK_DGRAM* = cint(2)
const SOCK_RAW* = cint(3)
const SOCK_SEQPACKET* = cint(5)
const SOCK_STREAM* = cint(1)
const SOL_SOCKET* = cint(65535)
const SOMAXCONN* = cint(128)
const SO_REUSEPORT* = cint(512)
const MSG_NOSIGNAL* = cint(131072)
const MSG_PEEK* = cint(2)
const MSG_TRUNC* = cint(16)
const MSG_WAITALL* = cint(64)
const AF_INET* = TSa_Family(2)
const AF_INET6* = TSa_Family(28)
const AF_UNIX* = TSa_Family(1)
const AF_UNSPEC* = TSa_Family(0)
const SHUT_RD* = cint(0)
const SHUT_RDWR* = cint(2)
const SHUT_WR* = cint(1)
# <sys/stat.h>
const S_IFBLK* = cint(24576)
const S_IFCHR* = cint(8192)
const S_IFDIR* = cint(16384)
const S_IFIFO* = cint(4096)
const S_IFLNK* = cint(40960)
const S_IFMT* = cint(61440)
const S_IFREG* = cint(32768)
const S_IFSOCK* = cint(49152)
const S_IRGRP* = cint(32)
const S_IROTH* = cint(4)
const S_IRUSR* = cint(256)
const S_IRWXG* = cint(56)
const S_IRWXO* = cint(7)
const S_IRWXU* = cint(448)
const S_ISGID* = cint(1024)
const S_ISUID* = cint(2048)
const S_ISVTX* = cint(512)
const S_IWGRP* = cint(16)
const S_IWOTH* = cint(2)
const S_IWUSR* = cint(128)
const S_IXGRP* = cint(8)
const S_IXOTH* = cint(1)
const S_IXUSR* = cint(64)
# <sys/statvfs.h>
const ST_RDONLY* = cint(1)
const ST_NOSUID* = cint(2)
# <sys/wait.h>
const WNOHANG* = cint(1)
const WUNTRACED* = cint(2)
# <spawn.h>
const POSIX_SPAWN_RESETIDS* = cint(1)
const POSIX_SPAWN_SETPGROUP* = cint(2)
const POSIX_SPAWN_SETSCHEDPARAM* = cint(4)
const POSIX_SPAWN_SETSCHEDULER* = cint(8)
const POSIX_SPAWN_SETSIGDEF* = cint(16)
const POSIX_SPAWN_SETSIGMASK* = cint(32)
# <stdio.h>
const IOFBF* = cint(0)
const IONBF* = cint(2)
# <time.h>
const CLOCKS_PER_SEC* = clong(100)
const CLOCK_REALTIME* = cint(1)
const TIMER_ABSTIME* = cint(4)
const CLOCK_MONOTONIC* = cint(4)
# <unistd.h>
const F_OK* = cint(0)
const R_OK* = cint(4)
const W_OK* = cint(2)
const X_OK* = cint(1)
const F_LOCK* = cint(1)
const F_TEST* = cint(3)
const F_TLOCK* = cint(2)
const F_ULOCK* = cint(0)
const PC_2_SYMLINKS* = cint(13)
const PC_ALLOC_SIZE_MIN* = cint(15)
const PC_ASYNC_IO* = cint(9)
const PC_CHOWN_RESTRICTED* = cint(6)
const PC_FILESIZEBITS* = cint(12)
const PC_LINK_MAX* = cint(0)
const PC_MAX_CANON* = cint(1)
const PC_MAX_INPUT* = cint(2)
const PC_NAME_MAX* = cint(3)
const PC_NO_TRUNC* = cint(7)
const PC_PATH_MAX* = cint(4)
const PC_PIPE_BUF* = cint(5)
const PC_PRIO_IO* = cint(10)
const PC_REC_INCR_XFER_SIZE* = cint(16)
const PC_REC_MIN_XFER_SIZE* = cint(18)
const PC_REC_XFER_ALIGN* = cint(19)
const PC_SYMLINK_MAX* = cint(14)
const PC_SYNC_IO* = cint(11)
const PC_VDISABLE* = cint(8)
const SC_2_C_BIND* = cint(108)
const SC_2_C_DEV* = cint(109)
const SC_2_CHAR_TERM* = cint(107)
const SC_2_FORT_DEV* = cint(110)
const SC_2_FORT_RUN* = cint(111)
const SC_2_LOCALEDEF* = cint(112)
const SC_2_PBS* = cint(113)
const SC_2_PBS_ACCOUNTING* = cint(114)
const SC_2_PBS_CHECKPOINT* = cint(115)
const SC_2_PBS_LOCATE* = cint(116)
const SC_2_PBS_MESSAGE* = cint(117)
const SC_2_PBS_TRACK* = cint(118)
const SC_2_SW_DEV* = cint(119)
const SC_2_UPE* = cint(120)
const SC_2_VERSION* = cint(121)
const SC_ADVISORY_INFO* = cint(54)
const SC_AIO_LISTIO_MAX* = cint(34)
const SC_AIO_MAX* = cint(35)
const SC_AIO_PRIO_DELTA_MAX* = cint(36)
const SC_ARG_MAX* = cint(0)
const SC_ASYNCHRONOUS_IO* = cint(21)
const SC_ATEXIT_MAX* = cint(55)
const SC_BARRIERS* = cint(56)
const SC_BC_BASE_MAX* = cint(57)
const SC_BC_DIM_MAX* = cint(58)
const SC_BC_SCALE_MAX* = cint(59)
const SC_BC_STRING_MAX* = cint(60)
const SC_CHILD_MAX* = cint(1)
const SC_CLK_TCK* = cint(2)
const SC_CLOCK_SELECTION* = cint(61)
const SC_COLL_WEIGHTS_MAX* = cint(62)
const SC_CPUTIME* = cint(63)
const SC_DELAYTIMER_MAX* = cint(37)
const SC_EXPR_NEST_MAX* = cint(64)
const SC_FSYNC* = cint(22)
const SC_GETGR_R_SIZE_MAX* = cint(50)
const SC_GETPW_R_SIZE_MAX* = cint(51)
const SC_HOST_NAME_MAX* = cint(65)
const SC_IOV_MAX* = cint(66)
const SC_IPV6* = cint(67)
const SC_JOB_CONTROL* = cint(5)
const SC_LINE_MAX* = cint(68)
const SC_LOGIN_NAME_MAX* = cint(52)
const SC_MAPPED_FILES* = cint(23)
const SC_MEMLOCK* = cint(24)
const SC_MEMLOCK_RANGE* = cint(25)
const SC_MEMORY_PROTECTION* = cint(26)
const SC_MESSAGE_PASSING* = cint(27)
const SC_MONOTONIC_CLOCK* = cint(69)
const SC_MQ_OPEN_MAX* = cint(13)
const SC_MQ_PRIO_MAX* = cint(14)
const SC_NGROUPS_MAX* = cint(3)
const SC_OPEN_MAX* = cint(4)
const SC_PAGE_SIZE* = cint(8)
const SC_PRIORITIZED_IO* = cint(28)
const SC_PRIORITY_SCHEDULING* = cint(101)
const SC_RAW_SOCKETS* = cint(70)
const SC_RE_DUP_MAX* = cint(73)
const SC_READER_WRITER_LOCKS* = cint(71)
const SC_REALTIME_SIGNALS* = cint(29)
const SC_REGEXP* = cint(72)
const SC_RTSIG_MAX* = cint(15)
const SC_SAVED_IDS* = cint(6)
const SC_SEM_NSEMS_MAX* = cint(16)
const SC_SEM_VALUE_MAX* = cint(17)
const SC_SEMAPHORES* = cint(30)
const SC_SHARED_MEMORY_OBJECTS* = cint(31)
const SC_SHELL* = cint(74)
const SC_SIGQUEUE_MAX* = cint(18)
const SC_SPAWN* = cint(75)
const SC_SPIN_LOCKS* = cint(76)
const SC_SPORADIC_SERVER* = cint(77)
const SC_SS_REPL_MAX* = cint(78)
const SC_STREAM_MAX* = cint(100)
const SC_SYMLOOP_MAX* = cint(79)
const SC_SYNCHRONIZED_IO* = cint(32)
const SC_THREAD_ATTR_STACKADDR* = cint(43)
const SC_THREAD_ATTR_STACKSIZE* = cint(44)
const SC_THREAD_CPUTIME* = cint(80)
const SC_THREAD_DESTRUCTOR_ITERATIONS* = cint(53)
const SC_THREAD_KEYS_MAX* = cint(38)
const SC_THREAD_PRIO_INHERIT* = cint(46)
const SC_THREAD_PRIO_PROTECT* = cint(47)
const SC_THREAD_PRIORITY_SCHEDULING* = cint(45)
const SC_THREAD_PROCESS_SHARED* = cint(48)
const SC_THREAD_SAFE_FUNCTIONS* = cint(49)
const SC_THREAD_SPORADIC_SERVER* = cint(81)
const SC_THREAD_STACK_MIN* = cint(39)
const SC_THREAD_THREADS_MAX* = cint(40)
const SC_THREADS* = cint(42)
const SC_TIMEOUTS* = cint(82)
const SC_TIMER_MAX* = cint(19)
const SC_TIMERS* = cint(33)
const SC_TRACE* = cint(83)
const SC_TRACE_EVENT_FILTER* = cint(84)
const SC_TRACE_EVENT_NAME_MAX* = cint(85)
const SC_TRACE_INHERIT* = cint(86)
const SC_TRACE_LOG* = cint(87)
const SC_TRACE_NAME_MAX* = cint(88)
const SC_TRACE_SYS_MAX* = cint(89)
const SC_TRACE_USER_EVENT_MAX* = cint(90)
const SC_TTY_NAME_MAX* = cint(41)
const SC_TYPED_MEMORY_OBJECTS* = cint(91)
const SC_TZNAME_MAX* = cint(20)
const SC_V6_ILP32_OFF32* = cint(92)
const SC_V6_ILP32_OFFBIG* = cint(93)
const SC_V6_LP64_OFF64* = cint(94)
const SC_V6_LPBIG_OFFBIG* = cint(95)
const SC_VERSION* = cint(7)
const SC_XBS5_ILP32_OFF32* = cint(92)
const SC_XBS5_ILP32_OFFBIG* = cint(93)
const SC_XBS5_LP64_OFF64* = cint(94)
const SC_XBS5_LPBIG_OFFBIG* = cint(95)
const SC_XOPEN_CRYPT* = cint(96)
const SC_XOPEN_ENH_I18N* = cint(97)
const SC_XOPEN_LEGACY* = cint(98)
const SC_XOPEN_REALTIME* = cint(99)
const SC_XOPEN_REALTIME_THREADS* = cint(102)
const SC_XOPEN_SHM* = cint(103)
const SC_XOPEN_STREAMS* = cint(104)
const SC_XOPEN_UNIX* = cint(105)
const SC_XOPEN_VERSION* = cint(106)
const SC_NPROCESSORS_ONLN* = cint(10)
const SEEK_SET* = cint(0)
const SEEK_CUR* = cint(1)
const SEEK_END* = cint(2)

View file

@ -7,10 +7,10 @@
# distribution, for details about the copyright.
#
{.deadCodeElim:on.}
{.deadCodeElim: on.} # dce option deprecated
const
hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
hasSpawnH = true # should exist for every Posix system nowadays
hasAioH = defined(linux)
when defined(linux) and not defined(android):
@ -43,6 +43,9 @@ type
Dirent* {.importc: "struct dirent",
header: "<dirent.h>", final, pure.} = object ## dirent_t struct
when defined(haiku):
d_dev*: Dev ## Device (not POSIX)
d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
d_ino*: Ino ## File serial number.
when defined(dragonfly):
# DragonflyBSD doesn't have `d_reclen` field.
@ -54,6 +57,9 @@ type
## (not POSIX)
when defined(linux) or defined(openbsd):
d_off*: Off ## Not an offset. Value that ``telldir()`` would return.
elif defined(haiku):
d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
d_reclen*: cushort ## Length of this record. (not POSIX)
d_name*: array[0..255, char] ## Name of entry.
@ -215,14 +221,14 @@ type
## For a typed memory object, the length in bytes.
## For other file types, the use of this field is
## unspecified.
when defined(macosx) or defined(android):
st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change.
else:
when StatHasNanoseconds:
st_atim*: Timespec ## Time of last access.
st_mtim*: Timespec ## Time of last data modification.
st_ctim*: Timespec ## Time of last status change.
else:
st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change.
st_blksize*: Blksize ## A file system-specific preferred I/O block size
## for this object. In some file system types, this
## may vary from file to file.
@ -335,8 +341,8 @@ type
Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec*: int ## Seconds.
tv_usec*: int ## Microseconds.
tv_sec*: Time ## Seconds.
tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object
Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",
@ -599,6 +605,10 @@ else:
MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
when defined(haiku):
const
SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team
when hasSpawnH:
when defined(linux):
# better be safe than sorry; Linux has this flag, macosx doesn't, don't

View file

@ -451,6 +451,9 @@ var POSIX_TYPED_MEM_ALLOCATE* {.importc: "POSIX_TYPED_MEM_ALLOCATE", header: "<s
var POSIX_TYPED_MEM_ALLOCATE_CONTIG* {.importc: "POSIX_TYPED_MEM_ALLOCATE_CONTIG", header: "<sys/mman.h>".}: cint
var POSIX_TYPED_MEM_MAP_ALLOCATABLE* {.importc: "POSIX_TYPED_MEM_MAP_ALLOCATABLE", header: "<sys/mman.h>".}: cint
# <sys/resource.h>
var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "<sys/resource.h>".}: cint
# <sys/select.h>
var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "<sys/select.h>".}: cint

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
{.deadCodeElim: on.}
{.deadCodeElim: on.} # dce option deprecated
import posix
type
@ -122,6 +122,21 @@ var
B9600* {.importc, header: "<termios.h>".}: Speed
B19200* {.importc, header: "<termios.h>".}: Speed
B38400* {.importc, header: "<termios.h>".}: Speed
B57600* {.importc, header: "<termios.h>".}: Speed
B115200* {.importc, header: "<termios.h>".}: Speed
B230400* {.importc, header: "<termios.h>".}: Speed
B460800* {.importc, header: "<termios.h>".}: Speed
B500000* {.importc, header: "<termios.h>".}: Speed
B576000* {.importc, header: "<termios.h>".}: Speed
B921600* {.importc, header: "<termios.h>".}: Speed
B1000000* {.importc, header: "<termios.h>".}: Speed
B1152000* {.importc, header: "<termios.h>".}: Speed
B1500000* {.importc, header: "<termios.h>".}: Speed
B2000000* {.importc, header: "<termios.h>".}: Speed
B2500000* {.importc, header: "<termios.h>".}: Speed
B3000000* {.importc, header: "<termios.h>".}: Speed
B3500000* {.importc, header: "<termios.h>".}: Speed
B4000000* {.importc, header: "<termios.h>".}: Speed
EXTA* {.importc, header: "<termios.h>".}: Speed
EXTB* {.importc, header: "<termios.h>".}: Speed
CSIZE* {.importc, header: "<termios.h>".}: Cflag

View file

@ -13,9 +13,6 @@ type
SortOrder* = enum ## sort order
Descending, Ascending
{.deprecated: [TSortOrder: SortOrder].}
proc `*`*(x: int, order: SortOrder): int {.inline.} =
## flips `x` if ``order == Descending``;
## if ``order == Ascending`` then `x` is returned.
@ -24,16 +21,20 @@ proc `*`*(x: int, order: SortOrder): int {.inline.} =
var y = order.ord - 1
result = (x xor y) - y
proc fill*[T](a: var openArray[T], first, last: Natural, value: T) =
## fills the array ``a[first..last]`` with `value`.
template fillImpl[T](a: var openArray[T], first, last: int, value: T) =
var x = first
while x <= last:
a[x] = value
inc(x)
proc fill*[T](a: var openArray[T], first, last: Natural, value: T) =
## fills the array ``a[first..last]`` with `value`.
fillImpl(a, first, last, value)
proc fill*[T](a: var openArray[T], value: T) =
## fills the array `a` with `value`.
fill(a, 0, a.high, value)
fillImpl(a, 0, a.high, value)
proc reverse*[T](a: var openArray[T], first, last: Natural) =
## reverses the array ``a[first..last]``.
@ -63,36 +64,72 @@ proc reversed*[T](a: openArray[T]): seq[T] =
## returns the reverse of the array `a`.
reversed(a, 0, a.high)
proc binarySearch*[T, K](a: openArray[T], key: K,
cmp: proc (x: T, y: K): int {.closure.}): int =
## binary search for `key` in `a`. Returns -1 if not found.
##
## `cmp` is the comparator function to use, the expected return values are
## the same as that of system.cmp.
if a.len == 0:
return -1
let len = a.len
if len == 1:
if cmp(a[0], key) == 0:
return 0
else:
return -1
if (len and (len - 1)) == 0:
# when `len` is a power of 2, a faster shr can be used.
var step = len shr 1
var cmpRes: int
while step > 0:
let i = result or step
cmpRes = cmp(a[i], key)
if cmpRes == 0:
return i
if cmpRes < 1:
result = i
step = step shr 1
if cmp(a[result], key) != 0: result = -1
else:
var b = len
var cmpRes: int
while result < b:
var mid = (result + b) shr 1
cmpRes = cmp(a[mid], key)
if cmpRes == 0:
return mid
if cmpRes < 0:
result = mid + 1
else:
b = mid
if result >= len or cmp(a[result], key) != 0: result = -1
proc binarySearch*[T](a: openArray[T], key: T): int =
## binary search for `key` in `a`. Returns -1 if not found.
var b = len(a)
while result < b:
var mid = (result + b) div 2
if a[mid] < key: result = mid + 1
else: b = mid
if result >= len(a) or a[result] != key: result = -1
binarySearch(a, key, cmp[T])
proc smartBinarySearch*[T](a: openArray[T], key: T): int =
## ``a.len`` must be a power of 2 for this to work.
var step = a.len div 2
while step > 0:
if a[result or step] <= key:
result = result or step
step = step shr 1
if a[result] != key: result = -1
proc smartBinarySearch*[T](a: openArray[T], key: T): int {.deprecated.} =
## **Deprecated since version 0.18.1**; Use ``binarySearch`` instead.
binarySearch(a, key, cmp[T])
const
onlySafeCode = true
proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int =
## same as binarySearch except that if key is not in `a` then this
## returns the location where `key` would be if it were. In other
## words if you have a sorted sequence and you call
proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
## Returns a position to the first element in the `a` that is greater than `key`, or last
## if no such element is found. In other words if you have a sorted sequence and you call
## insert(thing, elm, lowerBound(thing, elm))
## the sequence will still be sorted.
##
## `cmp` is the comparator function to use, the expected return values are
## The first version uses `cmp` to compare the elements. The expected return values are
## the same as that of system.cmp.
## The second version uses the default comparison function `cmp`.
##
## example::
##
@ -103,7 +140,7 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.})
var count = a.high - a.low + 1
var step, pos: int
while count != 0:
step = count div 2
step = count shr 1
pos = result + step
if cmp(a[pos], key) < 0:
result = pos + 1
@ -113,6 +150,36 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.})
proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T])
proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
## Returns a position to the first element in the `a` that is not less
## (i.e. greater or equal to) than `key`, or last if no such element is found.
## In other words if you have a sorted sequence and you call
## insert(thing, elm, upperBound(thing, elm))
## the sequence will still be sorted.
##
## The first version uses `cmp` to compare the elements. The expected return values are
## the same as that of system.cmp.
## The second version uses the default comparison function `cmp`.
##
## example::
##
## var arr = @[1,2,3,4,6,7,8,9]
## arr.insert(5, arr.upperBound(4))
## # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
result = a.low
var count = a.high - a.low + 1
var step, pos: int
while count != 0:
step = count shr 1
pos = result + step
if cmp(a[pos], key) <= 0:
result = pos + 1
count -= step + 1
else:
count = step
proc upperBound*[T](a: openArray[T], key: T): int = upperBound(a, key, cmp[T])
template `<-` (a, b) =
when false:
a = b
@ -359,10 +426,11 @@ when isMainModule:
var srt1 = [1,2,3,4,4,4,4,5]
var srt2 = ["iello","hello"]
var srt3 = [1.0,1.0,1.0]
var srt4: seq[int] = @[]
var srt4: seq[int]
assert srt1.isSorted(cmp) == true
assert srt2.isSorted(cmp) == false
assert srt3.isSorted(cmp) == true
assert srt4.isSorted(cmp) == true
var srtseq = newSeq[int]()
assert srtseq.isSorted(cmp) == true
# Tests for reversed
@ -391,7 +459,7 @@ proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
swap(arg[mFirst], arg[next])
mFirst += 1
next += 1
next += 1
if mFirst == mMiddle:
mMiddle = next
@ -443,7 +511,7 @@ proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int):
## the distance in amount of elements that the data should be rotated. Can be negative, can be any number.
##
## .. code-block:: nim
## var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
## var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
## list.rotateLeft(1 .. 8, 3)
## doAssert list == [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
let sliceLen = slice.b + 1 - slice.a
@ -472,12 +540,12 @@ proc rotatedLeft*[T](arg: openarray[T]; dist: int): seq[T] =
arg.rotatedInternal(0, distLeft, arg.len)
when isMainModule:
var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let list2 = list.rotatedLeft(1 ..< 9, 3)
var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let list2 = list.rotatedLeft(1 ..< 9, 3)
let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
doAssert list.rotateLeft(1 ..< 9, 3) == 6
doAssert list == expected
doAssert list == expected
doAssert list2 == @expected
var s0,s1,s2,s3,s4,s5 = "xxxabcdefgxxx"
@ -494,3 +562,45 @@ when isMainModule:
doAssert s4 == "xxxefgabcdxxx"
doAssert s5.rotateLeft(3 ..< 10, 11) == 6
doAssert s5 == "xxxefgabcdxxx"
block product:
doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input"
doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input"
doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element"
doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements"
doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements"
doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty"
block lowerBound:
doAssert lowerBound([1,2,4], 3, system.cmp[int]) == 2
doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4
doAssert lowerBound([1,2,3,10], 11) == 4
block upperBound:
doAssert upperBound([1,2,4], 3, system.cmp[int]) == 2
doAssert upperBound([1,2,2,3], 3, system.cmp[int]) == 4
doAssert upperBound([1,2,3,5], 3) == 3
block fillEmptySeq:
var s = newSeq[int]()
s.fill(0)
block testBinarySearch:
var noData: seq[int]
doAssert binarySearch(noData, 7) == -1
let oneData = @[1]
doAssert binarySearch(oneData, 1) == 0
doAssert binarySearch(onedata, 7) == -1
let someData = @[1,3,4,7]
doAssert binarySearch(someData, 1) == 0
doAssert binarySearch(somedata, 7) == 3
doAssert binarySearch(someData, -1) == -1
doAssert binarySearch(someData, 5) == -1
doAssert binarySearch(someData, 13) == -1
let moreData = @[1,3,5,7,4711]
doAssert binarySearch(moreData, -1) == -1
doAssert binarySearch(moreData, 1) == 0
doAssert binarySearch(moreData, 5) == 2
doAssert binarySearch(moreData, 6) == -1
doAssert binarySearch(moreData, 4711) == 4
doAssert binarySearch(moreData, 4712) == -1

View file

@ -10,6 +10,7 @@
include "system/inclrtl"
import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
import options, math
import asyncfutures except callSoon
import nativesockets, net, deques
@ -157,9 +158,6 @@ export asyncfutures, asyncstreams
## ----------------
##
## * The effect system (``raises: []``) does not work with async procedures.
## * Can't await in a ``except`` body
## * Forward declarations for async procs are broken,
## link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
# TODO: Check if yielded future is nil and throw a more meaningful exception
@ -168,8 +166,10 @@ type
timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks*: Deque[proc ()]
proc processTimers(p: PDispatcherBase; didSomeWork: var bool) {.inline.} =
#Process just part if timers at a step
proc processTimers(
p: PDispatcherBase, didSomeWork: var bool
): Option[int] {.inline.} =
# Pop the timers in the order in which they will expire (smaller `finishAt`).
var count = p.timers.len
let t = epochTime()
while count > 0 and t >= p.timers[0].finishAt:
@ -177,22 +177,25 @@ proc processTimers(p: PDispatcherBase; didSomeWork: var bool) {.inline.} =
dec count
didSomeWork = true
# Return the number of miliseconds in which the next timer will expire.
if p.timers.len == 0: return
let milisecs = (p.timers[0].finishAt - epochTime()) * 1000
return some(ceil(milisecs).int)
proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
while p.callbacks.len > 0:
var cb = p.callbacks.popFirst()
cb()
didSomeWork = true
proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
# If dispatcher has active timers this proc returns the timeout
# of the nearest timer. Returns `timeout` otherwise.
result = timeout
if p.timers.len > 0:
let timerTimeout = p.timers[0].finishAt
let curTime = epochTime()
if timeout == -1 or (curTime + (timeout / 1000)) > timerTimeout:
result = int((timerTimeout - curTime) * 1000)
if result < 0: result = 0
proc adjustTimeout(pollTimeout: int, nextTimer: Option[int]): int {.inline.} =
if nextTimer.isNone():
return pollTimeout
result = nextTimer.get()
if pollTimeout == -1: return
result = min(pollTimeout, result)
proc callSoon(cbproc: proc ()) {.gcsafe.}
@ -299,53 +302,53 @@ when defined(windows) or defined(nimdoc):
"No handles or timers registered in dispatcher.")
result = false
if p.handles.len != 0:
let at = p.adjustedTimeout(timeout)
var llTimeout =
if at == -1: winlean.INFINITE
else: at.int32
let nextTimer = processTimers(p, result)
let at = adjustTimeout(timeout, nextTimer)
var llTimeout =
if at == -1: winlean.INFINITE
else: at.int32
var lpNumberOfBytesTransferred: Dword
var lpCompletionKey: ULONG_PTR
var customOverlapped: PCustomOverlapped
let res = getQueuedCompletionStatus(p.ioPort,
addr lpNumberOfBytesTransferred, addr lpCompletionKey,
cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
result = true
var lpNumberOfBytesTransferred: Dword
var lpCompletionKey: ULONG_PTR
var customOverlapped: PCustomOverlapped
let res = getQueuedCompletionStatus(p.ioPort,
addr lpNumberOfBytesTransferred, addr lpCompletionKey,
cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
result = true
# http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
if res:
# This is useful for ensuring the reliability of the overlapped struct.
# http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
if res:
# This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, OSErrorCode(-1))
# If cell.data != nil, then system.protect(rawEnv(cb)) was called,
# so we need to dispose our `cb` environment, because it is not needed
# anymore.
if customOverlapped.data.cell.data != nil:
system.dispose(customOverlapped.data.cell)
GC_unref(customOverlapped)
else:
let errCode = osLastError()
if customOverlapped != nil:
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, OSErrorCode(-1))
# If cell.data != nil, then system.protect(rawEnv(cb)) was called,
# so we need to dispose our `cb` environment, because it is not needed
# anymore.
lpNumberOfBytesTransferred, errCode)
if customOverlapped.data.cell.data != nil:
system.dispose(customOverlapped.data.cell)
GC_unref(customOverlapped)
else:
let errCode = osLastError()
if customOverlapped != nil:
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, errCode)
if customOverlapped.data.cell.data != nil:
system.dispose(customOverlapped.data.cell)
GC_unref(customOverlapped)
else:
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
result = false
else: raiseOSError(errCode)
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
result = false
else: raiseOSError(errCode)
# Timer processing.
processTimers(p, result)
discard processTimers(p, result)
# Callback queue processing
processPendingCallbacks(p, result)
@ -1231,48 +1234,48 @@ else:
"No handles or timers registered in dispatcher.")
result = false
if not p.selector.isEmpty():
var keys: array[64, ReadyKey]
var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
for i in 0..<count:
var custom = false
let fd = keys[i].fd
let events = keys[i].events
var rLength = 0 # len(data.readList) after callback
var wLength = 0 # len(data.writeList) after callback
var keys: array[64, ReadyKey]
let nextTimer = processTimers(p, result)
var count = p.selector.selectInto(adjustTimeout(timeout, nextTimer), keys)
for i in 0..<count:
var custom = false
let fd = keys[i].fd
let events = keys[i].events
var rLength = 0 # len(data.readList) after callback
var wLength = 0 # len(data.writeList) after callback
if Event.Read in events or events == {Event.Error}:
processBasicCallbacks(fd, readList)
result = true
if Event.Read in events or events == {Event.Error}:
processBasicCallbacks(fd, readList)
result = true
if Event.Write in events or events == {Event.Error}:
processBasicCallbacks(fd, writeList)
result = true
if Event.Write in events or events == {Event.Error}:
processBasicCallbacks(fd, writeList)
result = true
if Event.User in events:
processBasicCallbacks(fd, readList)
if Event.User in events:
processBasicCallbacks(fd, readList)
custom = true
if rLength == 0:
p.selector.unregister(fd)
result = true
when ioselSupportedPlatform:
if (customSet * events) != {}:
custom = true
if rLength == 0:
p.selector.unregister(fd)
processCustomCallbacks(fd)
result = true
when ioselSupportedPlatform:
if (customSet * events) != {}:
custom = true
processCustomCallbacks(fd)
result = true
# because state `data` can be modified in callback we need to update
# descriptor events with currently registered callbacks.
if not custom:
var newEvents: set[Event] = {}
if rLength != -1 and wLength != -1:
if rLength > 0: incl(newEvents, Event.Read)
if wLength > 0: incl(newEvents, Event.Write)
p.selector.updateHandle(SocketHandle(fd), newEvents)
# because state `data` can be modified in callback we need to update
# descriptor events with currently registered callbacks.
if not custom:
var newEvents: set[Event] = {}
if rLength != -1 and wLength != -1:
if rLength > 0: incl(newEvents, Event.Read)
if wLength > 0: incl(newEvents, Event.Write)
p.selector.updateHandle(SocketHandle(fd), newEvents)
# Timer processing.
processTimers(p, result)
discard processTimers(p, result)
# Callback queue processing
processPendingCallbacks(p, result)
@ -1513,7 +1516,7 @@ proc poll*(timeout = 500) =
# Common procedures between current and upcoming asyncdispatch
include includes.asynccommon
proc sleepAsync*(ms: int): Future[void] =
proc sleepAsync*(ms: int | float): Future[void] =
## Suspends the execution of the current async procedure for the next
## ``ms`` milliseconds.
var retFuture = newFuture[void]("sleepAsync")
@ -1533,7 +1536,11 @@ proc withTimeout*[T](fut: Future[T], timeout: int): Future[bool] =
var timeoutFuture = sleepAsync(timeout)
fut.callback =
proc () =
if not retFuture.finished: retFuture.complete(true)
if not retFuture.finished:
if fut.failed:
retFuture.fail(fut.error)
else:
retFuture.complete(true)
timeoutFuture.callback =
proc () =
if not retFuture.finished: retFuture.complete(false)

View file

@ -78,7 +78,10 @@ proc getFileSize*(f: AsyncFile): int64 =
raiseOSError(osLastError())
result = (high shl 32) or low
else:
let curPos = lseek(f.fd.cint, 0, SEEK_CUR)
result = lseek(f.fd.cint, 0, SEEK_END)
f.offset = lseek(f.fd.cint, curPos, SEEK_SET)
assert(f.offset == curPos)
proc newAsyncFile*(fd: AsyncFd): AsyncFile =
## Creates `AsyncFile` with a previously opened file descriptor `fd`.
@ -88,7 +91,7 @@ proc newAsyncFile*(fd: AsyncFd): AsyncFile =
proc openAsync*(filename: string, mode = fmRead): AsyncFile =
## Opens a file specified by the path in ``filename`` using
## the specified ``mode`` asynchronously.
## the specified FileMode ``mode`` asynchronously.
when defined(windows) or defined(nimdoc):
let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
let desiredAccess = getDesiredAccess(mode)
@ -281,6 +284,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
result = false # We still want this callback to be called.
elif res == 0:
# EOF
f.offset = lseek(fd.cint, 0, SEEK_CUR)
retFuture.complete("")
else:
readBuffer.setLen(res)

View file

@ -304,6 +304,7 @@ proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
raise newException(ReplyError, "Reply has no file size.")
await getFile(ftp, destFile, fileSize, onProgressChanged)
destFile.close()
proc doUpload(ftp: AsyncFtpClient, file: File,
onProgressChanged: ProgressChangedProc) {.async.} =

View file

@ -27,8 +27,6 @@ type
FutureError* = object of Exception
cause*: FutureBase
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
when not defined(release):
var currentID = 0
@ -177,7 +175,7 @@ proc fail*[T](future: Future[T], error: ref Exception) =
if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
future.callbacks.call()
proc clearCallbacks(future: FutureBase) =
proc clearCallbacks*(future: FutureBase) =
future.callbacks.function = nil
future.callbacks.next = nil
@ -221,10 +219,10 @@ proc getHint(entry: StackTraceEntry): string =
## We try to provide some hints about stack trace entries that the user
## may not be familiar with, in particular calls inside the stdlib.
result = ""
if entry.procname == "processPendingCallbacks":
if entry.procname == cstring"processPendingCallbacks":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks"
elif entry.procname == "poll":
elif entry.procname == cstring"poll":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Processes asynchronous completion events"

View file

@ -60,9 +60,6 @@ type
reusePort: bool
maxBody: int ## The maximum content-length that will be read for the body.
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance.
@ -132,6 +129,20 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc parseUppercaseMethod(name: string): HttpMethod =
result =
case name
of "GET": HttpGet
of "POST": HttpPost
of "HEAD": HttpHead
of "PUT": HttpPut
of "DELETE": HttpDelete
of "PATCH": HttpPatch
of "OPTIONS": HttpOptions
of "CONNECT": HttpConnect
of "TRACE": HttpTrace
else: raise newException(ValueError, "Invalid HTTP method " & name)
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
@ -175,8 +186,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
case i
of 0:
try:
# TODO: this is likely slow.
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
request.reqMethod = parseUppercaseMethod(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return

View file

@ -11,7 +11,7 @@
## *************
## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
import macros, strutils
import macros, strutils, asyncfutures
proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
# Skips a nest of StmtList's.
@ -26,6 +26,8 @@ proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
template createCb(retFutureSym, iteratorNameSym,
strName, identName, futureVarCompletions: untyped) =
bind finished
var nameIterVar = iteratorNameSym
#{.push stackTrace: off.}
proc identName {.closure.} =
@ -60,52 +62,6 @@ template createCb(retFutureSym, iteratorNameSym,
identName()
#{.pop.}
proc generateExceptionCheck(futSym,
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
if tryStmt.kind == nnkNilLit:
result = rootReceiver
else:
var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
let errorNode = newDotExpr(futSym, newIdentNode("error"))
for i in 1 ..< tryStmt.len:
let exceptBranch = tryStmt[i]
if exceptBranch[0].kind == nnkStmtList:
exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
else:
var exceptIdentCount = 0
var ifCond: NimNode
for i in 0 ..< exceptBranch.len:
let child = exceptBranch[i]
if child.kind == nnkIdent:
let cond = infix(errorNode, "of", child)
if exceptIdentCount == 0:
ifCond = cond
else:
ifCond = infix(ifCond, "or", cond)
else:
break
exceptIdentCount.inc
expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
# -> -> else: raise futSym.error
exceptionChecks.add((newIdentNode("true"),
newNimNode(nnkRaiseStmt).add(errorNode)))
# Read the future if there is no error.
# -> else: futSym.read
let elseNode = newNimNode(nnkElse, fromNode)
elseNode.add newNimNode(nnkStmtList, fromNode)
elseNode[0].add rootReceiver
let ifBody = newStmtList()
ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
ifBody.add newIfStmt(exceptionChecks)
ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
result = newIfStmt(
(newDotExpr(futSym, newIdentNode("failed")), ifBody)
)
result.add elseNode
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
rootReceiver: untyped, fromNode: NimNode) =
@ -121,8 +77,7 @@ template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
# -> future<x>.read
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
fromNode)
result.add rootReceiver
template createVar(result: var NimNode, futSymName: string,
asyncProc: NimNode,
@ -152,8 +107,8 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
)
proc processBody(node, retFutureSym: NimNode,
subTypeIsVoid: bool, futureVarIdents: seq[NimNode],
tryStmt: NimNode): NimNode {.compileTime.} =
subTypeIsVoid: bool,
futureVarIdents: seq[NimNode]): NimNode {.compileTime.} =
#echo(node.treeRepr)
result = node
case node.kind
@ -171,7 +126,7 @@ proc processBody(node, retFutureSym: NimNode,
result.add newCall(newIdentNode("complete"), retFutureSym)
else:
let x = node[0].processBody(retFutureSym, subTypeIsVoid,
futureVarIdents, tryStmt)
futureVarIdents)
if x.kind == nnkYieldStmt: result.add x
else:
result.add newCall(newIdentNode("complete"), retFutureSym, x)
@ -179,7 +134,7 @@ proc processBody(node, retFutureSym: NimNode,
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
return # Don't process the children of this return stmt
of nnkCommand, nnkCall:
if node[0].kind == nnkIdent and node[0].ident == !"await":
if node[0].kind == nnkIdent and node[0].eqIdent("await"):
case node[1].kind
of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand:
# await x
@ -192,7 +147,7 @@ proc processBody(node, retFutureSym: NimNode,
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node.len > 1 and node[1].kind == nnkCommand and
node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
node[1][0].kind == nnkIdent and node[1][0].eqIdent("await"):
# foo await x
var newCommand = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
@ -201,16 +156,16 @@ proc processBody(node, retFutureSym: NimNode,
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
result.createVar("future" & $node[0][0].ident, node[0][2][1],
result.createVar("future" & node[0][0].strVal, node[0][2][1],
newVarSection[0][2], newVarSection, node)
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
if node[1][0].eqIdent("await"):
# x = await y
var newAsgn = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
@ -218,74 +173,22 @@ proc processBody(node, retFutureSym: NimNode,
of nnkDiscardStmt:
# discard await x
if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
node[0][0].ident == !"await":
node[0][0].eqIdent("await"):
var newDiscard = node
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
newDiscard[0], newDiscard, node)
of nnkTryStmt:
# try: await x; except: ...
result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: untyped) =
var temp = n
n[0] = tryBody
tryBody = temp
# Transform ``except`` body.
# TODO: Could we perform some ``await`` transformation here to get it
# working in ``except``?
tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid,
futureVarIdents, nil)
proc processForTry(n: NimNode, i: var int,
res: NimNode): bool {.compileTime.} =
## Transforms the body of the tryStmt. Does not transform the
## body in ``except``.
## Returns true if the tryStmt node was transformed into an ifStmt.
result = false
var skipped = n.skipStmtList()
while i < skipped.len:
var processed = processBody(skipped[i], retFutureSym,
subTypeIsVoid, futureVarIdents, n)
# Check if we transformed the node into an exception check.
# This suggests skipped[i] contains ``await``.
if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
processed = processed.skipUntilStmtList()
expectKind(processed, nnkStmtList)
expectKind(processed[2][1], nnkElse)
i.inc
if not processForTry(n, i, processed[2][1][0]):
# We need to wrap the nnkElse nodes back into a tryStmt.
# As they are executed if an exception does not happen
# inside the awaited future.
# The following code will wrap the nodes inside the
# original tryStmt.
wrapInTry(n, processed[2][1][0])
res.add processed
result = true
else:
res.add skipped[i]
i.inc
var i = 0
if not processForTry(node, i, result):
# If the tryStmt hasn't been transformed we can just put the body
# back into it.
wrapInTry(node, result)
return
else: discard
for i in 0 ..< result.len:
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
futureVarIdents, nil)
futureVarIdents)
proc getName(node: NimNode): string {.compileTime.} =
case node.kind
of nnkPostfix:
return $node[1].ident
return node[1].strVal
of nnkIdent:
return $node.ident
return node.strVal
of nnkEmpty:
return "anonymous"
else:
@ -296,7 +199,7 @@ proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
for i in 1 ..< len(params):
expectKind(params[i], nnkIdentDefs)
if params[i][1].kind == nnkBracketExpr and
($params[i][1][0].ident).normalize == "futurevar":
params[i][1][0].eqIdent("futurevar"):
result.add(params[i][0])
proc isInvalidReturnType(typeName: string): bool =
@ -323,7 +226,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
let fut = repr(returnType[0])
verifyReturnType(fut)
baseType = returnType[1]
elif returnType.kind in nnkCallKinds and $returnType[0] == "[]":
elif returnType.kind in nnkCallKinds and returnType[0].eqIdent("[]"):
let fut = repr(returnType[1])
verifyReturnType(fut)
baseType = returnType[2]
@ -333,7 +236,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
verifyReturnType(repr(returnType))
let subtypeIsVoid = returnType.kind == nnkEmpty or
(baseType.kind == nnkIdent and returnType[1].ident == !"void")
(baseType.kind == nnkIdent and returnType[1].eqIdent("void"))
let futureVarIdents = getFutureVarIdents(prc.params)
@ -348,7 +251,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr, prc.body).add(
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
newIdentNode("newFuture"),
subRetType),
newLit(prcName)))) # Get type from return type of this proc
@ -360,7 +263,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
futureVarIdents, nil)
futureVarIdents)
# don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty:
procBody.add(createFutureVarCompletions(futureVarIdents, nil))
@ -424,8 +327,8 @@ macro async*(prc: untyped): untyped =
## Macro which processes async procedures into the appropriate
## iterators and yield statements.
if prc.kind == nnkStmtList:
result = newStmtList()
for oneProc in prc:
result = newStmtList()
result.add asyncSingleProc(oneProc)
else:
result = asyncSingleProc(prc)
@ -448,30 +351,30 @@ proc stripAwait(node: NimNode): NimNode =
case node.kind
of nnkCommand, nnkCall:
if node[0].kind == nnkIdent and node[0].ident == !"await":
if node[0].kind == nnkIdent and node[0].eqIdent("await"):
node[0] = emptyNoopSym
elif node.len > 1 and node[1].kind == nnkCommand and
node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
node[1][0].kind == nnkIdent and node[1][0].eqIdent("await"):
# foo await x
node[1][0] = emptyNoopSym
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
# var x = await y
node[0][2][0] = emptyNoopSym
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
if node[1][0].eqIdent("await"):
# x = await y
node[1][0] = emptyNoopSym
else: discard
of nnkDiscardStmt:
# discard await x
if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
node[0][0].ident == !"await":
node[0][0].eqIdent("await"):
node[0][0] = emptyNoopSym
else: discard
@ -480,9 +383,9 @@ proc stripAwait(node: NimNode): NimNode =
proc splitParamType(paramType: NimNode, async: bool): NimNode =
result = paramType
if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
let firstAsync = "async" in ($paramType[1].ident).normalize
let secondAsync = "async" in ($paramType[2].ident).normalize
if paramType.kind == nnkInfix and paramType[0].strVal in ["|", "or"]:
let firstAsync = "async" in paramType[1].strVal.normalize
let secondAsync = "async" in paramType[2].strVal.normalize
if firstAsync:
result = paramType[if async: 1 else: 2]

View file

@ -134,8 +134,6 @@ type
protocol: Protocol
AsyncSocket* = ref AsyncSocketDesc
{.deprecated: [PAsyncSocket: AsyncSocket].}
proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
@ -201,7 +199,7 @@ when defineSsl:
flags: set[SocketFlag]) {.async.} =
let len = bioCtrlPending(socket.bioOut)
if len > 0:
var data = newStringOfCap(len)
var data = newString(len)
let read = bioRead(socket.bioOut, addr data[0], len)
assert read != 0
if read < 0:
@ -495,8 +493,6 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
## protocol uses ``\r\L`` to delimit a new line.
assert SocketFlag.Peek notin flags ## TODO:
assert(not resString.mget.isNil(),
"String inside resString future needs to be initialised")
result = newFuture[void]("asyncnet.recvLineInto")
# TODO: Make the async transformation check for FutureVar params and complete
@ -659,7 +655,7 @@ when defineSsl:
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
handshake: SslHandshakeType,
hostname: string = nil) =
hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname
@ -674,7 +670,7 @@ when defineSsl:
case handshake
of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname):
if hostname.len > 0 and not isIpAddress(hostname):
# Set the SNI address for this connection. This call can fail if
# we're not using TLSv1+.
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)

View file

@ -52,7 +52,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
result = newString(total)
var
var
i = 0
r = 0
currLine = 0
@ -76,7 +76,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
currLine = 0
if i < s.len-1:
let
let
a = ord(s[i])
b = ord(s[i+1])
result[r] = cb64[a shr 2]
@ -130,11 +130,11 @@ proc decode*(s: string): string =
# total is an upper bound, as we will skip arbitrary whitespace:
result = newString(total)
var
var
i = 0
r = 0
while true:
while s[i] in Whitespace: inc(i)
while i < s.len and s[i] in Whitespace: inc(i)
if i < s.len-3:
let
a = s[i].decodeByte

View file

@ -64,8 +64,6 @@ type
methodPost, ## query uses the POST method
methodGet ## query uses the GET method
{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
proc cgiError*(msg: string) {.noreturn.} =
## raises an ECgi exception with message `msg`.
var e: ref CgiError
@ -97,11 +95,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
var name = ""
var value = ""
# decode everything in one pass:
while data[i] != '\0':
while i < data.len:
setLen(name, 0) # reuse memory
while true:
while i < data.len:
case data[i]
of '\0': break
of '%':
var x = 0
handleHexChar(data[i+1], x)
@ -112,15 +109,16 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
of '=', '&': break
else: add(name, data[i])
inc(i)
if data[i] != '=': cgiError("'=' expected")
if i >= data.len or data[i] != '=': cgiError("'=' expected")
inc(i) # skip '='
setLen(value, 0) # reuse memory
while true:
while i < data.len:
case data[i]
of '%':
var x = 0
handleHexChar(data[i+1], x)
handleHexChar(data[i+2], x)
if i+2 < data.len:
handleHexChar(data[i+1], x)
handleHexChar(data[i+2], x)
inc(i, 2)
add(value, chr(x))
of '+': add(value, ' ')
@ -128,19 +126,18 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
else: add(value, data[i])
inc(i)
yield (name.TaintedString, value.TaintedString)
if data[i] == '&': inc(i)
elif data[i] == '\0': break
else: cgiError("'&' expected")
if i < data.len:
if data[i] == '&': inc(i)
else: cgiError("'&' expected")
iterator decodeData*(allowedMethods: set[RequestMethod] =
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
## Reads and decodes CGI data and yields the (name, value) pairs the
## data consists of. If the client does not use a method listed in the
## `allowedMethods` set, an `ECgi` exception is raised.
var data = getEncodedData(allowedMethods)
if not isNil(data):
for key, value in decodeData(data):
yield (key, value)
let data = getEncodedData(allowedMethods)
for key, value in decodeData(data):
yield (key, value)
proc readData*(allowedMethods: set[RequestMethod] =
{methodNone, methodPost, methodGet}): StringTableRef =

View file

@ -74,18 +74,19 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
var newByte = 0
block blockX:
while newbyte < key.len:
if it.key[newbyte] != key[newbyte]:
newotherbits = it.key[newbyte].ord xor key[newbyte].ord
let ch = if newbyte < it.key.len: it.key[newbyte] else: '\0'
if ch != key[newbyte]:
newotherbits = ch.ord xor key[newbyte].ord
break blockX
inc newbyte
if it.key[newbyte] != '\0':
if newbyte < it.key.len:
newotherbits = it.key[newbyte].ord
else:
return it
while (newOtherBits and (newOtherBits-1)) != 0:
newOtherBits = newOtherBits and (newOtherBits-1)
newOtherBits = newOtherBits xor 255
let ch = it.key[newByte]
let ch = if newByte < it.key.len: it.key[newByte] else: '\0'
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
var inner: Node[T]
@ -162,18 +163,20 @@ proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
var n = rawInsert(c, key)
result = c.count == oldCount
proc inc*(c: var CritBitTree[int]; key: string) =
## counts the 'key'.
let oldCount = c.count
proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) =
## increments `c[key]` by `val`.
var n = rawInsert(c, key)
if c.count == oldCount:
# not a new key:
inc n.val
inc n.val, val
proc incl*(c: var CritBitTree[void], key: string) =
## includes `key` in `c`.
discard rawInsert(c, key)
proc incl*[T](c: var CritBitTree[T], key: string, val: T) =
## inserts `key` with value `val` into `c`.
var n = rawInsert(c, key)
n.val = val
proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
## puts a (key, value)-pair into `t`.
var n = rawInsert(c, key)
@ -321,10 +324,14 @@ proc `$`*[T](c: CritBitTree[T]): string =
const avgItemLen = 16
result = newStringOfCap(c.count * avgItemLen)
result.add("{")
for key, val in pairs(c):
if result.len > 1: result.add(", ")
result.add($key)
when T isnot void:
when T is void:
for key in keys(c):
if result.len > 1: result.add(", ")
result.addQuoted(key)
else:
for key, val in pairs(c):
if result.len > 1: result.add(", ")
result.addQuoted(key)
result.add(": ")
result.addQuoted(val)
result.add("}")
@ -351,3 +358,37 @@ when isMainModule:
assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
var c = CritBitTree[int]()
c.inc("a")
assert c["a"] == 1
c.inc("a", 4)
assert c["a"] == 5
c.inc("a", -5)
assert c["a"] == 0
c.inc("b", 2)
assert c["b"] == 2
c.inc("c", 3)
assert c["c"] == 3
c.inc("a", 1)
assert c["a"] == 1
var cf = CritBitTree[float]()
cf.incl("a", 1.0)
assert cf["a"] == 1.0
cf.incl("b", 2.0)
assert cf["b"] == 2.0
cf.incl("c", 3.0)
assert cf["c"] == 3.0
assert cf.len == 3
cf.excl("c")
assert cf.len == 2

View file

@ -38,7 +38,7 @@
## Note: For inter thread communication use
## a `Channel <channels.html>`_ instead.
import math
import math, typetraits
type
Deque*[T] = object
@ -160,16 +160,15 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
emptyCheck(deq)
result = deq.data[(deq.tail - 1) and deq.mask]
template default[T](t: typedesc[T]): T =
var v: T
v
template destroy(x: untyped) =
reset(x)
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the first element of the `deq`.
emptyCheck(deq)
dec deq.count
result = deq.data[deq.head]
deq.data[deq.head] = default(type(result))
destroy(deq.data[deq.head])
deq.head = (deq.head + 1) and deq.mask
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
@ -178,7 +177,34 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
dec deq.count
deq.tail = (deq.tail - 1) and deq.mask
result = deq.data[deq.tail]
deq.data[deq.tail] = default(type(result))
destroy(deq.data[deq.tail])
proc clear*[T](deq: var Deque[T]) {.inline.} =
## Resets the deque so that it is empty.
for el in mitems(deq): destroy(el)
deq.count = 0
deq.tail = deq.head
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
## Remove `fromFirst` elements from the front of the deque and
## `fromLast` elements from the back. If the supplied number of
## elements exceeds the total number of elements in the deque,
## the deque will remain empty.
##
## Any user defined destructors
if fromFirst + fromLast > deq.count:
clear(deq)
return
for i in 0 ..< fromFirst:
destroy(deq.data[deq.head])
deq.head = (deq.head + 1) and deq.mask
for i in 0 ..< fromLast:
destroy(deq.data[deq.tail])
deq.tail = (deq.tail - 1) and deq.mask
dec deq.count, fromFirst + fromLast
proc `$`*[T](deq: Deque[T]): string =
## Turn a deque into its string representation.
@ -215,6 +241,22 @@ when isMainModule:
assert deq.find(6) >= 0
assert deq.find(789) < 0
block:
var d = initDeque[int](1)
d.addLast 7
d.addLast 8
d.addLast 10
d.addFirst 5
d.addFirst 2
d.addFirst 1
d.addLast 20
d.shrink(fromLast = 2)
doAssert($d == "[1, 2, 5, 7, 8]")
d.shrink(2, 1)
doAssert($d == "[5, 7]")
d.shrink(2, 2)
doAssert d.len == 0
for i in -2 .. 10:
if i in deq:
assert deq.contains(i) and deq.find(i) >= 0

View file

@ -184,7 +184,7 @@ proc missingOrExcl*(s: var IntSet, key: int) : bool =
## `key` is removed from `s` and false is returned.
var count = s.elems
exclImpl(s, key)
result = count == s.elems
result = count == s.elems
proc containsOrIncl*(s: var IntSet, key: int): bool =
## returns true if `s` contains `key`, otherwise `key` is included in `s`
@ -212,7 +212,10 @@ proc initIntSet*: IntSet =
#newSeq(result.data, InitIntSetSize)
#result.max = InitIntSetSize-1
result.data = nil
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0
result.counter = 0
result.head = nil
@ -222,7 +225,10 @@ proc clear*(result: var IntSet) =
#setLen(result.data, InitIntSetSize)
#for i in 0..InitIntSetSize-1: result.data[i] = nil
#result.max = InitIntSetSize-1
result.data = nil
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0
result.counter = 0
result.head = nil
@ -234,7 +240,10 @@ proc assign*(dest: var IntSet, src: IntSet) =
## copies `src` to `dest`. `dest` does not need to be initialized by
## `initIntSet`.
if src.elems <= src.a.len:
dest.data = nil
when defined(nimNoNilSeqs):
dest.data = @[]
else:
dest.data = nil
dest.max = 0
dest.counter = src.counter
dest.head = nil
@ -247,11 +256,9 @@ proc assign*(dest: var IntSet, src: IntSet) =
var it = src.head
while it != nil:
var h = it.key and dest.max
while dest.data[h] != nil: h = nextTry(h, dest.max)
assert(dest.data[h] == nil)
var n: PTrunk
new(n)
n.next = dest.head
@ -259,7 +266,6 @@ proc assign*(dest: var IntSet, src: IntSet) =
n.bits = it.bits
dest.head = n
dest.data[h] = n
it = it.next
proc union*(s1, s2: IntSet): IntSet =
@ -315,7 +321,7 @@ proc len*(s: IntSet): int {.inline.} =
for _ in s:
inc(result)
proc card*(s: IntSet): int {.inline.} =
proc card*(s: IntSet): int {.inline.} =
## alias for `len() <#len>` _.
result = s.len()
@ -361,7 +367,7 @@ when isMainModule:
x.incl(1056)
x.incl(1044)
x.excl(1044)
x.excl(1044)
assert x.containsOrIncl(888) == false
assert 888 in x

View file

@ -25,6 +25,28 @@ import macros
when not defined(nimhygiene):
{.pragma: dirty.}
macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped =
## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple
## substitution in macro arguments such as
## https://github.com/nim-lang/Nim/issues/7187
## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp``
## except when ``letAssigneable`` is false (eg to handle openArray) where
## it just forwards ``exp`` unchanged
expectKind(expAlias, nnkIdent)
var val = exp
result = newStmtList()
# If `exp` is not a symbol we evaluate it once here and then use the temporary
# symbol as alias
if exp.kind != nnkSym and letAssigneable:
val = genSym()
result.add(newLetStmt(val, exp))
result.add(
newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)],
body = val, procType = nnkTemplateDef))
proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
## Takes several sequences' items and returns them inside a new sequence.
##
@ -161,7 +183,6 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
## assert numbers.distribute(6)[0] == @[1, 2]
## assert numbers.distribute(6)[5] == @[7]
assert(not s.isNil, "`s` can't be nil")
if num < 2:
result = @[s]
return
@ -496,13 +517,17 @@ template toSeq*(iter: untyped): untyped =
## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9]
# Note: see also `mapIt` for explanation of some of the implementation
# subtleties.
when compiles(iter.len):
var i = 0
var result = newSeq[type(iter)](iter.len)
for x in iter:
result[i] = x
inc i
result
block:
evalOnceAs(iter2, iter, true)
var result = newSeq[type(iter)](iter2.len)
var i = 0
for x in iter2:
result[i] = x
inc i
result
else:
var result: seq[type(iter)] = @[]
for x in iter:
@ -635,8 +660,7 @@ template mapIt*(s, typ, op: untyped): untyped =
result.add(op)
result
template mapIt*(s, op: untyped): untyped =
template mapIt*(s: typed, op: untyped): untyped =
## Convenience template around the ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
@ -653,19 +677,24 @@ template mapIt*(s, op: untyped): untyped =
block:
var it{.inject.}: type(items(s));
op))
var result: seq[outType]
when compiles(s.len):
let t = s
var i = 0
result = newSeq[outType](s.len)
for it {.inject.} in t:
result[i] = op
i += 1
block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580
# BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
# (`error: use of undeclared identifier`) instead of Nim compile errors
evalOnceAs(s2, s, compiles((let _ = s)))
var i = 0
var result = newSeq[outType](s2.len)
for it {.inject.} in s2:
result[i] = op
i += 1
result
else:
result = @[]
var result: seq[outType] = @[]
for it {.inject.} in s:
result.add(op)
result
result
template applyIt*(varSeq, op: untyped) =
## Convenience template around the mutable ``apply`` proc to reduce typing.
@ -752,6 +781,14 @@ macro mapLiterals*(constructor, op: untyped;
when isMainModule:
import strutils
# helper for testing double substitution side effects which are handled
# by `evalOnceAs`
var counter = 0
proc identity[T](a:T):auto=
counter.inc
a
block: # concat test
let
s1 = @[1, 2, 3]
@ -854,20 +891,20 @@ when isMainModule:
doAssert numbers.distribute(6)[0] == @[1, 2]
doAssert numbers.distribute(6)[5] == @[7]
let a = @[1, 2, 3, 4, 5, 6, 7]
doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
doAssert a.distribute(6, false) == @[
doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
doAssert a.distribute(6, false) == @[
@[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
doAssert a.distribute(8, false) == a.distribute(8, true)
doAssert a.distribute(8, false) == a.distribute(8, true)
doAssert a.distribute(90, false) == a.distribute(90, true)
var b = @[0]
for f in 1 .. 25: b.add(f)
@ -997,6 +1034,12 @@ when isMainModule:
result = true)
assert odd_numbers == @[1, 3, 5, 7, 9]
block:
# tests https://github.com/nim-lang/Nim/issues/7187
counter = 0
let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3))
doAssert ret == @[1, 2]
doAssert counter == 1
block: # foldl tests
let
numbers = @[5, 9, 11]
@ -1023,10 +1066,12 @@ when isMainModule:
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimiscool"
block: # mapIt tests
block: # mapIt + applyIt test
counter = 0
var
nums = @[1, 2, 3, 4]
strings = nums.mapIt($(4 * it))
strings = nums.identity.mapIt($(4 * it))
doAssert counter == 1
nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15
assert strings[2] == "12"
@ -1044,5 +1089,51 @@ when isMainModule:
doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
block: # mapIt with openArray
counter = 0
proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10)
doAssert foo([identity(1),identity(2)]) == @[10, 20]
doAssert counter == 2
block: # mapIt with direct openArray
proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10)
counter = 0
doAssert foo1(openArray[int]([identity(1),identity(2)])) == @[10,20]
doAssert counter == 2
# Corner cases (openArray litterals should not be common)
template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10)
counter = 0
doAssert foo2(openArray[int]([identity(1),identity(2)])) == @[10,20]
# TODO: this fails; not sure how to fix this case
# doAssert counter == 2
counter = 0
doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1,2]
# ditto
# doAssert counter == 2
block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468
# NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type
# of elements)
doAssert: not compiles(mapIt(@[], it))
doAssert: not compiles(mapIt([], it))
doAssert newSeq[int](0).mapIt(it) == @[]
block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580
let s2 = [1,2].mapIt(it)
doAssert s2 == @[1,2]
block:
counter = 0
doAssert [1,2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40]
# https://github.com/nim-lang/Nim/issues/7187 test case
doAssert counter == 1
block: # mapIt with invalid RHS for `let` (#8566)
type X = enum
A, B
doAssert mapIt(X, $it) == @["A", "B"]
when not defined(testing):
echo "Finished doc tests"

View file

@ -11,7 +11,7 @@
## ordered hash set.
##
## Hash sets are different from the `built in set type
## <manual.html#set-type>`_. Sets allow you to store any value that can be
## <manual.html#types-set-type>`_. Sets allow you to store any value that can be
## `hashed <hashes.html>`_ and they don't contain duplicate entries.
##
## **Note**: The data types declared here have *value semantics*: This means
@ -74,7 +74,7 @@ proc isValid*[A](s: HashSet[A]): bool =
## proc savePreferences(options: HashSet[string]) =
## assert options.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds!
result = not s.data.isNil
result = s.data.len > 0
proc len*[A](s: HashSet[A]): int =
## Returns the number of keys in `s`.
@ -120,6 +120,13 @@ iterator items*[A](s: HashSet[A]): A =
for h in 0..high(s.data):
if isFilled(s.data[h].hcode): yield s.data[h].key
proc hash*[A](s: HashSet[A]): Hash =
## hashing of HashSet
assert s.isValid, "The set needs to be initialized."
for h in 0..high(s.data):
result = result xor s.data[h].hcode
result = !$result
const
growthFactor = 2
@ -340,6 +347,18 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
assert other.isValid, "The set `other` needs to be initialized."
for item in other: discard exclImpl(s, item)
proc pop*[A](s: var HashSet[A]): A =
## Remove and return an arbitrary element from the set `s`.
##
## Raises KeyError if the set `s` is empty.
##
for h in 0..high(s.data):
if isFilled(s.data[h].hcode):
result = s.data[h].key
excl(s, result)
return result
raise newException(KeyError, "set is empty")
proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
## Includes `key` in the set `s` and tells if `key` was added to `s`.
##
@ -635,7 +654,7 @@ proc isValid*[A](s: OrderedSet[A]): bool =
## proc saveTarotCards(cards: OrderedSet[int]) =
## assert cards.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds!
result = not s.data.isNil
result = s.data.len > 0
proc len*[A](s: OrderedSet[A]): int {.inline.} =
## Returns the number of keys in `s`.
@ -690,6 +709,13 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs:
yield s.data[h].key
proc hash*[A](s: OrderedSet[A]): Hash =
## hashing of OrderedSet
assert s.isValid, "The set needs to be initialized."
forAllOrderedPairs:
result = result !& s.data[h].hcode
result = !$result
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
assert s.isValid, "The set needs to be initialized"
forAllOrderedPairs:

View file

@ -136,11 +136,13 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
## procedure.
##
## The ``mapper`` takes 3 arguments:
## #. ``key`` - the current key, if it exists, or the key passed to
## ``withKey`` otherwise;
## #. ``val`` - the current value, if the key exists, or default value
## of the type otherwise;
## #. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise.
##
## 1. ``key`` - the current key, if it exists, or the key passed to
## ``withKey`` otherwise;
## 2. ``val`` - the current value, if the key exists, or default value
## of the type otherwise;
## 3. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise.
##
## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change
## the mapping of the key or delete it from the table.
## When adding a value, make sure to set ``pairExists`` to ``true`` along

View file

@ -158,6 +158,12 @@ template getOrDefaultImpl(t, key): untyped =
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
template getOrDefaultImpl(t, key, default: untyped): untyped =
mixin rawGet
var hc: Hash
var index = rawGet(t, key, hc)
result = if index >= 0: t.data[index].val else: default
proc `[]`*[A, B](t: Table[A, B], key: A): B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
@ -175,10 +181,18 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} =
## instead.
get(t, key)
proc getOrDefault*[A, B](t: Table[A, B], key: A): B = getOrDefaultImpl(t, key)
proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the
## default initialization value for type `B` is returned (e.g. 0 for any
## integer type).
getOrDefaultImpl(t, key)
template withValue*[A, B](t: var Table[A, B], key: A,
value, body: untyped) =
proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default`
## is returned.
getOrDefaultImpl(t, key, default)
template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
## retrieves the value at ``t[key]``.
## `value` can be modified in the scope of the ``withValue`` call.
##
@ -266,7 +280,7 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B =
if isFilled(t.data[h].hcode): yield t.data[h].val
proc del*[A, B](t: var Table[A, B], key: A) =
## deletes `key` from hash table `t`.
## deletes `key` from hash table `t`. Does nothing if the key does not exist.
delImpl()
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
@ -325,8 +339,7 @@ proc initTable*[A, B](initialSize=64): Table[A, B] =
result.counter = 0
newSeq(result.data, initialSize)
proc toTable*[A, B](pairs: openArray[(A,
B)]): Table[A, B] =
proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
## creates a new hash table that contains the given `pairs`.
result = initTable[A, B](rightSize(pairs.len))
for key, val in items(pairs): result[key] = val
@ -410,7 +423,16 @@ proc mget*[A, B](t: TableRef[A, B], key: A): var B {.deprecated.} =
## Use ```[]``` instead.
t[][key]
proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = getOrDefault(t[], key)
proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the
## default initialization value for type `B` is returned (e.g. 0 for any
## integer type).
getOrDefault(t[], key)
proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default`
## is returned.
getOrDefault(t[], key, default)
proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
@ -435,7 +457,7 @@ proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
t[].add(key, val)
proc del*[A, B](t: TableRef[A, B], key: A) =
## deletes `key` from hash table `t`.
## deletes `key` from hash table `t`. Does nothing if the key does not exist.
t[].del(key)
proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
@ -562,8 +584,15 @@ proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} =
get(t, key)
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the
## default initialization value for type `B` is returned (e.g. 0 for any
## integer type).
getOrDefaultImpl(t, key)
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default`
## is returned.
getOrDefaultImpl(t, key, default)
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`.
@ -630,8 +659,7 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
result.last = -1
newSeq(result.data, initialSize)
proc toOrderedTable*[A, B](pairs: openArray[(A,
B)]): OrderedTable[A, B] =
proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
## creates a new ordered hash table that contains the given `pairs`.
result = initOrderedTable[A, B](rightSize(pairs.len))
for key, val in items(pairs): result[key] = val
@ -657,8 +685,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
hs = nxts
return true
proc sort*[A, B](t: var OrderedTable[A, B],
cmp: proc (x,y: (A, B)): int) =
proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) =
## sorts `t` according to `cmp`. This modifies the internal list
## that kept the insertion order, so insertion order is lost after this
## call but key lookup and insertions remain possible after `sort` (in
@ -748,8 +775,16 @@ proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B {.deprecated.} =
result = t[][key]
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the
## default initialization value for type `B` is returned (e.g. 0 for any
## integer type).
getOrDefault(t[], key)
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, `default`
## is returned.
getOrDefault(t[], key, default)
proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
## returning a value which can be modified.
@ -802,8 +837,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
elif isNil(t): result = false
else: result = s[] == t[]
proc sort*[A, B](t: OrderedTableRef[A, B],
cmp: proc (x,y: (A, B)): int) =
proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) =
## sorts `t` according to `cmp`. This modifies the internal list
## that kept the insertion order, so insertion order is lost after this
## call but key lookup and insertions remain possible after `sort` (in
@ -811,7 +845,8 @@ proc sort*[A, B](t: OrderedTableRef[A, B],
t[].sort(cmp)
proc del*[A, B](t: var OrderedTable[A, B], key: A) =
## deletes `key` from ordered hash table `t`. O(n) complexity.
## deletes `key` from ordered hash table `t`. O(n) complexity. Does nothing
## if the key does not exist.
var n: OrderedKeyValuePairSeq[A, B]
newSeq(n, len(t.data))
var h = t.first
@ -830,7 +865,8 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) =
h = nxt
proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
## deletes `key` from ordered hash table `t`. O(n) complexity.
## deletes `key` from ordered hash table `t`. O(n) complexity. Does nothing
## if the key does not exist.
t[].del(key)
# ------------------------------ count tables -------------------------------
@ -916,9 +952,17 @@ proc mget*[A](t: var CountTable[A], key: A): var int {.deprecated.} =
ctget(t, key)
proc getOrDefault*[A](t: CountTable[A], key: A): int =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, 0 (the
## default initialization value of `int`), is returned.
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
proc getOrDefault*[A](t: CountTable[A], key: A, default: int): int =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the
## integer value of `default` is returned.
var index = rawGet(t, key)
result = if index >= 0: t.data[index].val else: default
proc hasKey*[A](t: CountTable[A], key: A): bool =
## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0
@ -1073,8 +1117,15 @@ proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
result = t[][key]
proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, 0 (the
## default initialization value of `int`), is returned.
result = t[].getOrDefault(key)
proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
## retrieves the value at ``t[key]`` iff `key` is in `t`. Otherwise, the
## integer value of `default` is returned.
result = t[].getOrDefault(key, default)
proc hasKey*[A](t: CountTableRef[A], key: A): bool =
## returns true iff `key` is in the table `t`.
result = t[].hasKey(key)
@ -1267,7 +1318,7 @@ when isMainModule:
#lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here
#lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode
doAssert 0 == t.getOrDefault(testKey)
t.inc(testKey,3)
t.inc(testKey, 3)
doAssert 3 == t.getOrDefault(testKey)
block:
@ -1278,7 +1329,7 @@ when isMainModule:
doAssert clearTable[42] == "asd"
clearTable.clear()
doAssert(not clearTable.hasKey(123123))
doAssert clearTable.getOrDefault(42) == nil
doAssert clearTable.getOrDefault(42) == ""
block: #5482
var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
@ -1334,3 +1385,21 @@ when isMainModule:
block: # CountTable.smallest
let t = toCountTable([0, 0, 5, 5, 5])
doAssert t.smallest == (0, 2)
block:
var tp: Table[string, string] = initTable[string, string]()
doAssert "test1" == tp.getOrDefault("test1", "test1")
tp["test2"] = "test2"
doAssert "test2" == tp.getOrDefault("test2", "test1")
var tr: TableRef[string, string] = newTable[string, string]()
doAssert "test1" == tr.getOrDefault("test1", "test1")
tr["test2"] = "test2"
doAssert "test2" == tr.getOrDefault("test2", "test1")
var op: OrderedTable[string, string] = initOrderedTable[string, string]()
doAssert "test1" == op.getOrDefault("test1", "test1")
op["test2"] = "test2"
doAssert "test2" == op.getOrDefault("test2", "test1")
var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]()
doAssert "test1" == orf.getOrDefault("test1", "test1")
orf["test2"] = "test2"
doAssert "test2" == orf.getOrDefault("test2", "test1")

View file

@ -10,12 +10,11 @@
## the ``graphics`` module.
import strutils
from algorithm import binarySearch
type
Color* = distinct int ## a color stored as RGB
{.deprecated: [TColor: Color].}
proc `==` *(a, b: Color): bool {.borrow.}
## compares two colors.
@ -373,37 +372,28 @@ proc `$`*(c: Color): string =
## converts a color into its textual representation. Example: ``#00FF00``.
result = '#' & toHex(int(c), 6)
proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
var mid = (a + b) div 2
var c = cmp(x[mid].name, y)
if c < 0: a = mid + 1
elif c > 0: b = mid - 1
else: return mid
result = - 1
proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int =
result = cmpIgnoreCase(x.name, y)
proc parseColor*(name: string): Color =
## parses `name` to a color value. If no valid color could be
## parsed ``EInvalidValue`` is raised.
## parsed ``EInvalidValue`` is raised. Case insensitive.
if name[0] == '#':
result = Color(parseHexInt(name))
else:
var idx = binaryStrSearch(colorNames, name)
var idx = binarySearch(colorNames, name, colorNameCmp)
if idx < 0: raise newException(ValueError, "unknown color: " & name)
result = colorNames[idx][1]
proc isColor*(name: string): bool =
## returns true if `name` is a known color name or a hexadecimal color
## prefixed with ``#``.
## prefixed with ``#``. Case insensitive.
if name[0] == '#':
for i in 1 .. name.len-1:
if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false
result = true
else:
result = binaryStrSearch(colorNames, name) >= 0
result = binarySearch(colorNames, name, colorNameCmp) >= 0
proc rgb*(r, g, b: range[0..255]): Color =
## constructs a color from RGB values.

View file

@ -25,7 +25,9 @@ type
Complex* = tuple[re, im: float]
## a complex number, consisting of a real and an imaginary part
{.deprecated: [TComplex: Complex].}
const
im*: Complex = (re: 0.0, im: 1.0)
## The imaginary unit. √-1.
proc toComplex*(x: SomeInteger): Complex =
## Convert some integer ``x`` to a complex number.

View file

@ -38,8 +38,18 @@ when defined(macosx) or defined(bsd):
importc: "sysctl", nodecl.}
when defined(genode):
proc affinitySpaceTotal(): cuint {.
importcpp: "genodeEnv->cpu().affinity_space().total()".}
include genode/env
proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
importcpp: "@->cpu().affinity_space().total()".}
when defined(haiku):
{.emit: "#include <OS.h>".}
type
SystemInfo {.importc: "system_info", bycopy.} = object
cpuCount {.importc: "cpu_count".}: uint32
proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info".}
proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has.
@ -83,7 +93,11 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
result = sysconf(SC_NPROC_ONLN)
elif defined(genode):
result = affinitySpaceTotal().int
result = runtimeEnv.affinitySpaceTotal().int
elif defined(haiku):
var sysinfo: SystemInfo
if getSystemInfo(addr sysinfo) == 0:
result = sysinfo.cpuCount.int
else:
result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 0

View file

@ -30,7 +30,7 @@ proc destroySemaphore(cv: var Semaphore) {.inline.} =
deinitCond(cv.c)
deinitLock(cv.L)
proc await(cv: var Semaphore) =
proc blockUntil(cv: var Semaphore) =
acquire(cv.L)
while cv.counter <= 0:
wait(cv.c, cv.L)
@ -81,7 +81,7 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
fence()
b.interest = true
fence()
while b.left != b.entered: await(b.cv)
while b.left != b.entered: blockUntil(b.cv)
destroySemaphore(b.cv)
{.pop.}
@ -89,8 +89,6 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
# ----------------------------------------------------------------------------
type
foreign* = object ## a region that indicates the pointer comes from a
## foreign thread heap.
AwaitInfo = object
cv: Semaphore
idx: int
@ -99,7 +97,7 @@ type
FlowVarBaseObj = object of RootObj
ready, usesSemaphore, awaited: bool
cv: Semaphore #\
# for 'awaitAny' support
# for 'blockUntilAny' support
ai: ptr AwaitInfo
idx: int
data: pointer # we incRef and unref it to keep it alive; note this MUST NOT
@ -130,12 +128,12 @@ type
q: ToFreeQueue
readyForTask: Semaphore
proc await*(fv: FlowVarBase) =
proc blockUntil*(fv: FlowVarBase) =
## waits until the value for the flowVar arrives. Usually it is not necessary
## to call this explicitly.
if fv.usesSemaphore and not fv.awaited:
fv.awaited = true
await(fv.cv)
blockUntil(fv.cv)
destroySemaphore(fv.cv)
proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
@ -143,7 +141,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
w.data = data
w.f = fn
signal(w.taskArrived)
await(w.taskStarted)
blockUntil(w.taskStarted)
result = true
proc cleanFlowVars(w: ptr Worker) =
@ -168,12 +166,21 @@ proc wakeupWorkerToProcessQueue(w: ptr Worker) =
signal(w.q.empty)
signal(w.taskArrived)
proc attach(fv: FlowVarBase; i: int): bool =
acquire(fv.cv.L)
if fv.cv.counter <= 0:
fv.idx = i
result = true
else:
result = false
release(fv.cv.L)
proc finished(fv: FlowVarBase) =
doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
doAssert fv.ai.isNil, "flowVar is still attached to an 'blockUntilAny'"
# we have to protect against the rare cases where the owner of the flowVar
# simply disregards the flowVar and yet the "flowVar" has not yet written
# anything to it:
await(fv)
blockUntil(fv)
if fv.data.isNil: return
let owner = cast[ptr Worker](fv.owner)
let q = addr(owner.q)
@ -182,7 +189,7 @@ proc finished(fv: FlowVarBase) =
#echo "EXHAUSTED!"
release(q.lock)
wakeupWorkerToProcessQueue(owner)
await(q.empty)
blockUntil(q.empty)
acquire(q.lock)
q.data[q.len] = cast[pointer](fv.data)
inc q.len
@ -213,7 +220,7 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
## to ``action``. Note that due to Nim's parameter passing semantics this
## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
## sometimes be more efficient than ``^``.
await(fv)
blockUntil(fv)
when T is string or T is seq:
action(cast[T](fv.data))
elif T is ref:
@ -222,49 +229,50 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
action(fv.blob)
finished(fv)
proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
## blocks until the value is available and then returns this value.
await(fv)
result = cast[foreign ptr T](fv.data)
blockUntil(fv)
result = cast[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)
blockUntil(fv)
let src = cast[ref T](fv.data)
deepCopy result, src
proc `^`*[T](fv: FlowVar[T]): T =
## blocks until the value is available and then returns this value.
await(fv)
blockUntil(fv)
when T is string or T is seq:
# XXX closures? deepCopy?
result = cast[T](fv.data)
else:
result = fv.blob
proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
## awaits any of the given flowVars. Returns the index of one flowVar for
## which a value arrived. A flowVar only supports one call to 'awaitAny' at
## the same time. That means if you await([a,b]) and await([b,c]) the second
## call will only await 'c'. If there is no flowVar left to be able to wait
## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at
## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second
## call will only blockUntil 'c'. If there is no flowVar left to be able to wait
## on, -1 is returned.
## **Note**: This results in non-deterministic behaviour and so should be
## avoided.
## **Note**: This results in non-deterministic behaviour and should be avoided.
var ai: AwaitInfo
ai.cv.initSemaphore()
var conflicts = 0
result = -1
for i in 0 .. flowVars.high:
if cas(addr flowVars[i].ai, nil, addr ai):
flowVars[i].idx = i
if not attach(flowVars[i], i):
result = i
break
else:
inc conflicts
if conflicts < flowVars.len:
await(ai.cv)
result = ai.idx
if result < 0:
blockUntil(ai.cv)
result = ai.idx
for i in 0 .. flowVars.high:
discard cas(addr flowVars[i].ai, addr ai, nil)
else:
result = -1
destroySemaphore(ai.cv)
proc isReady*(fv: FlowVarBase): bool =
@ -318,10 +326,10 @@ proc slave(w: ptr Worker) {.thread.} =
w.ready = true
readyWorker = w
signal(gSomeReady)
await(w.taskArrived)
blockUntil(w.taskArrived)
# XXX Somebody needs to look into this (why does this assertion fail
# in Visual Studio?)
when not defined(vcc): assert(not w.ready)
when not defined(vcc) and not defined(tcc): assert(not w.ready)
withLock numSlavesLock:
inc numSlavesRunning
@ -343,7 +351,7 @@ proc distinguishedSlave(w: ptr Worker) {.thread.} =
else:
w.ready = true
signal(w.readyForTask)
await(w.taskArrived)
blockUntil(w.taskArrived)
assert(not w.ready)
w.f(w, w.data)
if w.q.len != 0: w.cleanFlowVars
@ -491,7 +499,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
# on the current thread instead.
var self = addr(workersData[localThreadId-1])
fn(self, data)
await(self.taskStarted)
blockUntil(self.taskStarted)
return
if isSlave:
@ -516,7 +524,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
inc numSlavesWaiting
await(gSomeReady)
blockUntil(gSomeReady)
if isSlave:
withLock numSlavesLock:
@ -534,7 +542,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
release(distinguishedLock)
while true:
if selectWorker(addr(distinguishedData[id]), fn, data): break
await(distinguishedData[id].readyForTask)
blockUntil(distinguishedData[id].readyForTask)
proc sync*() =
@ -547,7 +555,7 @@ proc sync*() =
if not allReady: break
allReady = allReady and workersData[i].ready
if allReady: break
await(gSomeReady)
blockUntil(gSomeReady)
inc toRelease
for i in 0 ..< toRelease:

View file

@ -25,16 +25,16 @@ proc parseCookies*(s: string): StringTableRef =
result = newStringTable(modeCaseInsensitive)
var i = 0
while true:
while s[i] == ' ' or s[i] == '\t': inc(i)
while i < s.len and (s[i] == ' ' or s[i] == '\t'): inc(i)
var keystart = i
while s[i] != '=' and s[i] != '\0': inc(i)
while i < s.len and s[i] != '=': inc(i)
var keyend = i-1
if s[i] == '\0': break
if i >= s.len: break
inc(i) # skip '='
var valstart = i
while s[i] != ';' and s[i] != '\0': inc(i)
while i < s.len and s[i] != ';': inc(i)
result[substr(s, keystart, keyend)] = substr(s, valstart, i-1)
if s[i] == '\0': break
if i >= s.len: break
inc(i) # skip ';'
proc setCookie*(key, value: string, domain = "", path = "",
@ -51,26 +51,26 @@ proc setCookie*(key, value: string, domain = "", path = "",
if secure: result.add("; Secure")
if httpOnly: result.add("; HttpOnly")
proc setCookie*(key, value: string, expires: DateTime,
proc setCookie*(key, value: string, expires: DateTime|Time,
domain = "", path = "", noName = false,
secure = false, httpOnly = false): string =
## Creates a command in the format of
## ``Set-Cookie: key=value; Domain=...; ...``
##
## **Note:** UTC is assumed as the timezone for ``expires``.
return setCookie(key, value, domain, path,
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
noname, secure, httpOnly)
when isMainModule:
var tim = fromUnix(getTime().toUnix + 76 * (60 * 60 * 24))
let expire = fromUnix(0) + 1.seconds
let cookie = setCookie("test", "value", tim.utc)
when not defined(testing):
echo cookie
let start = "Set-Cookie: test=value; Expires="
assert cookie[0..start.high] == start
let cookies = [
setCookie("test", "value", expire),
setCookie("test", "value", expire.local),
setCookie("test", "value", expire.utc)
]
let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT"
doAssert cookies == [expected, expected, expected]
let table = parseCookies("uid=1; kp=2")
assert table["uid"] == "1"
assert table["kp"] == "2"
doAssert table["uid"] == "1"
doAssert table["kp"] == "2"

View file

@ -43,6 +43,10 @@ when defined(windows):
{.warning: "ucontext coroutine backend is not available on windows, defaulting to fibers.".}
when defined(nimCoroutinesSetjmp):
{.warning: "setjmp coroutine backend is not available on windows, defaulting to fibers.".}
elif defined(haiku):
const coroBackend = CORO_BACKEND_SETJMP
when defined(nimCoroutinesUcontext):
{.warning: "ucontext coroutine backend is not available on haiku, defaulting to setjmp".}
elif defined(nimCoroutinesSetjmp) or defined(nimCoroutinesSetjmpBundled):
const coroBackend = CORO_BACKEND_SETJMP
else:
@ -55,21 +59,21 @@ when coroBackend == CORO_BACKEND_FIBERS:
elif coroBackend == CORO_BACKEND_UCONTEXT:
type
stack_t {.importc, header: "<sys/ucontext.h>".} = object
stack_t {.importc, header: "<ucontext.h>".} = object
ss_sp: pointer
ss_flags: int
ss_size: int
ucontext_t {.importc, header: "<sys/ucontext.h>".} = object
ucontext_t {.importc, header: "<ucontext.h>".} = object
uc_link: ptr ucontext_t
uc_stack: stack_t
Context = ucontext_t
proc getcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
proc setcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<sys/ucontext.h>", varargs.}
proc getcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
proc setcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<ucontext.h>", varargs.}
elif coroBackend == CORO_BACKEND_SETJMP:
proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn, importc: "narch_$1", fastcall.}
@ -111,7 +115,12 @@ elif coroBackend == CORO_BACKEND_SETJMP:
when defined(unix):
# GLibc fails with "*** longjmp causes uninitialized stack frame ***" because
# our custom stacks are not initialized to a magic value.
{.passC: "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"}
when defined(osx):
# workaround: error: The deprecated ucontext routines require _XOPEN_SOURCE to be defined
const extra = " -D_XOPEN_SOURCE"
else:
const extra = ""
{.passC: "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0" & extra.}
const
CORO_CREATED = 0
@ -190,7 +199,7 @@ proc switchTo(current, to: CoroutinePtr) =
elif to.state == CORO_CREATED:
# Coroutine is started.
coroExecWithStack(runCurrentTask, to.stack.bottom)
doAssert false
#doAssert false
else:
{.error: "Invalid coroutine backend set.".}
# Execution was just resumed. Restore frame information and set active stack.

View file

@ -45,8 +45,10 @@ proc endsWith*(s, suffix: cstring): bool {.noSideEffect,
proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect,
rtl, extern: "csuCmpIgnoreStyle".} =
## Compares two strings normalized (i.e. case and
## underscores do not matter). Returns:
## Semantically the same as ``cmp(normalize($a), normalize($b))``. It
## is just optimized to not allocate temporary strings. This should
## NOT be used to compare Nim identifier names. use `macros.eqIdent`
## for that. Returns:
##
## | 0 iff a == b
## | < 0 iff a < b

View file

@ -83,9 +83,6 @@ type
foreignKey*: bool ## is this a foreign key?
DbColumns* = seq[DbColumn]
{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect,
FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].}
template sql*(query: string): SqlQuery =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:

View file

@ -126,6 +126,8 @@ type
OpenBSD
DragonFlyBSD
Haiku
const
LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware,
@ -166,6 +168,8 @@ proc detectOsImpl(d: Distribution): bool =
of Distribution.Solaris:
let uname = toLowerAscii(uname())
result = ("sun" in uname) or ("solaris" in uname)
of Distribution.Haiku:
result = defined(haiku)
else:
let dd = toLowerAscii($d)
result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release())
@ -224,6 +228,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
result = ("pacman -S " & p, true)
else:
result = ("<your package manager here> install " & p, true)
elif defined(haiku):
result = ("pkgman install " & p, true)
else:
result = ("brew install " & p, false)

View file

@ -10,14 +10,55 @@
## This module implements the ability to access symbols from shared
## libraries. On POSIX this uses the ``dlsym`` mechanism, on
## Windows ``LoadLibrary``.
##
## Examples
## --------
##
## Loading a simple C function
## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
##
## The following example demonstrates loading a function called 'greet'
## from a library that is determined at runtime based upon a language choice.
## If the library fails to load or the function 'greet' is not found,
## it quits with a failure error code.
##
## .. code-block::nim
##
## import dynlib
##
## type
## greetFunction = proc(): cstring {.gcsafe, stdcall.}
##
## let lang = stdin.readLine()
##
## let lib = case lang
## of "french":
## loadLib("french.dll")
## else:
## loadLib("english.dll")
##
## if lib == nil:
## echo "Error loading library"
## quit(QuitFailure)
##
## let greet = cast[greetFunction](lib.symAddr("greet"))
##
## if greet == nil:
## echo "Error loading 'greet' function from library"
## quit(QuitFailure)
##
## let greeting = greet()
##
## echo greeting
##
## unloadLib(lib)
##
import strutils
type
LibHandle* = pointer ## a handle to a dynamically loaded library
{.deprecated: [TLibHandle: LibHandle].}
proc loadLib*(path: string, global_symbols=false): LibHandle {.gcsafe.}
## loads a library from `path`. Returns nil if the library could not
## be loaded.
@ -96,6 +137,28 @@ when defined(posix):
proc symAddr(lib: LibHandle, name: cstring): pointer =
return dlsym(lib, name)
elif defined(nintendoswitch):
#
# =========================================================================
# Nintendo switch DevkitPro sdk does not have these. Raise an error if called.
# =========================================================================
#
proc dlclose(lib: LibHandle) =
raise newException(OSError, "dlclose not implemented on Nintendo Switch!")
proc dlopen(path: cstring, mode: int): LibHandle =
raise newException(OSError, "dlopen not implemented on Nintendo Switch!")
proc dlsym(lib: LibHandle, name: cstring): pointer =
raise newException(OSError, "dlsym not implemented on Nintendo Switch!")
proc loadLib(path: string, global_symbols=false): LibHandle =
raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
proc loadLib(): LibHandle =
raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
proc unloadLib(lib: LibHandle) =
raise newException(OSError, "unloadLib not implemented on Nintendo Switch!")
proc symAddr(lib: LibHandle, name: cstring): pointer =
raise newException(OSError, "symAddr not implemented on Nintendo Switch!")
elif defined(windows) or defined(dos):
#
# =======================================================================

View file

@ -27,8 +27,6 @@ type
EncodingError* = object of ValueError ## exception that is raised
## for encoding errors
{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].}
when defined(windows):
proc eqEncodingNames(a, b: string): bool =
var i = 0
@ -36,7 +34,7 @@ when defined(windows):
while i < a.len and j < b.len:
if a[i] in {'-', '_'}: inc i
if b[j] in {'-', '_'}: inc j
if a[i].toLower != b[j].toLower: return false
if i < a.len and j < b.len and a[i].toLowerAscii != b[j].toLowerAscii: return false
inc i
inc j
result = i == a.len and j == b.len
@ -257,7 +255,7 @@ when defined(windows):
else:
when defined(haiku):
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
const iconvDll = "libiconv.so"
elif defined(macosx):
const iconvDll = "libiconv.dylib"
else:
@ -274,6 +272,8 @@ else:
const EILSEQ = 86.cint
elif defined(solaris):
const EILSEQ = 88.cint
elif defined(haiku):
const EILSEQ = -2147454938.cint
var errno {.importc, header: "<errno.h>".}: cint
@ -334,7 +334,7 @@ when defined(windows):
if s.len == 0: return ""
# educated guess of capacity:
var cap = s.len + s.len shr 2
result = newStringOfCap(cap*2)
result = newString(cap*2)
# convert to utf-16 LE
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
@ -349,7 +349,7 @@ when defined(windows):
lpWideCharStr = nil,
cchWideChar = cint(0))
# and do the conversion properly:
result = newStringOfCap(cap*2)
result = newString(cap*2)
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len),
@ -366,7 +366,7 @@ when defined(windows):
if int(c.dest) == 1200: return
# otherwise the fun starts again:
cap = s.len + s.len shr 2
var res = newStringOfCap(cap)
var res = newString(cap)
m = wideCharToMultiByte(
codePage = c.dest,
dwFlags = 0'i32,
@ -384,7 +384,7 @@ when defined(windows):
lpMultiByteStr = nil,
cbMultiByte = cint(0))
# and do the conversion properly:
res = newStringOfCap(cap)
res = newString(cap)
m = wideCharToMultiByte(
codePage = c.dest,
dwFlags = 0'i32,

View file

@ -43,9 +43,6 @@ type
s: seq[EventHandler]
EventError* = object of ValueError
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
proc initEventHandler*(name: string): EventHandler =
## Initializes an EventHandler with the specified name and returns it.
result.handlers = @[]

View file

@ -10,9 +10,9 @@
## Floating-point environment. Handling of floating-point rounding and
## exceptions (overflow, division by zero, etc.).
{.deadCodeElim:on.}
{.deadCodeElim: on.} # dce option deprecated
when defined(Posix) and not defined(haiku):
when defined(Posix):
{.passl: "-lm".}
var
@ -102,32 +102,33 @@ proc feupdateenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
## represented by object pointed to by `envp` and raise exceptions
## according to saved exceptions.
var FP_RADIX_INTERNAL {. importc: "FLT_RADIX" header: "<float.h>" .} : int
const
FLT_RADIX = 2 ## the radix of the exponent representation
template fpRadix* : int = FP_RADIX_INTERNAL
FLT_MANT_DIG = 24 ## the number of base FLT_RADIX digits in the mantissa part of a float
FLT_DIG = 6 ## the number of digits of precision of a float
FLT_MIN_EXP = -125 # the minimum value of base FLT_RADIX in the exponent part of a float
FLT_MAX_EXP = 128 # the maximum value of base FLT_RADIX in the exponent part of a float
FLT_MIN_10_EXP = -37 ## the minimum value in base 10 of the exponent part of a float
FLT_MAX_10_EXP = 38 ## the maximum value in base 10 of the exponent part of a float
FLT_MIN = 1.17549435e-38'f32 ## the minimum value of a float
FLT_MAX = 3.40282347e+38'f32 ## the maximum value of a float
FLT_EPSILON = 1.19209290e-07'f32 ## the difference between 1 and the least value greater than 1 of a float
DBL_MANT_DIG = 53 ## the number of base FLT_RADIX digits in the mantissa part of a double
DBL_DIG = 15 ## the number of digits of precision of a double
DBL_MIN_EXP = -1021 ## the minimum value of base FLT_RADIX in the exponent part of a double
DBL_MAX_EXP = 1024 ## the maximum value of base FLT_RADIX in the exponent part of a double
DBL_MIN_10_EXP = -307 ## the minimum value in base 10 of the exponent part of a double
DBL_MAX_10_EXP = 308 ## the maximum value in base 10 of the exponent part of a double
DBL_MIN = 2.2250738585072014E-308 ## the minimal value of a double
DBL_MAX = 1.7976931348623157E+308 ## the minimal value of a double
DBL_EPSILON = 2.2204460492503131E-16 ## the difference between 1 and the least value greater than 1 of a double
template fpRadix* : int = FLT_RADIX
## The (integer) value of the radix used to represent any floating
## point type on the architecture used to build the program.
var FLT_MANT_DIG {. importc: "FLT_MANT_DIG" header: "<float.h>" .} : int
var FLT_DIG {. importc: "FLT_DIG" header: "<float.h>" .} : int
var FLT_MIN_EXP {. importc: "FLT_MIN_EXP" header: "<float.h>" .} : int
var FLT_MAX_EXP {. importc: "FLT_MAX_EXP" header: "<float.h>" .} : int
var FLT_MIN_10_EXP {. importc: "FLT_MIN_10_EXP" header: "<float.h>" .} : int
var FLT_MAX_10_EXP {. importc: "FLT_MAX_10_EXP" header: "<float.h>" .} : int
var FLT_MIN {. importc: "FLT_MIN" header: "<float.h>" .} : cfloat
var FLT_MAX {. importc: "FLT_MAX" header: "<float.h>" .} : cfloat
var FLT_EPSILON {. importc: "FLT_EPSILON" header: "<float.h>" .} : cfloat
var DBL_MANT_DIG {. importc: "DBL_MANT_DIG" header: "<float.h>" .} : int
var DBL_DIG {. importc: "DBL_DIG" header: "<float.h>" .} : int
var DBL_MIN_EXP {. importc: "DBL_MIN_EXP" header: "<float.h>" .} : int
var DBL_MAX_EXP {. importc: "DBL_MAX_EXP" header: "<float.h>" .} : int
var DBL_MIN_10_EXP {. importc: "DBL_MIN_10_EXP" header: "<float.h>" .} : int
var DBL_MAX_10_EXP {. importc: "DBL_MAX_10_EXP" header: "<float.h>" .} : int
var DBL_MIN {. importc: "DBL_MIN" header: "<float.h>" .} : cdouble
var DBL_MAX {. importc: "DBL_MAX" header: "<float.h>" .} : cdouble
var DBL_EPSILON {. importc: "DBL_EPSILON" header: "<float.h>" .} : cdouble
template mantissaDigits*(T : typedesc[float32]) : int = FLT_MANT_DIG
## Number of digits (in base ``floatingPointRadix``) in the mantissa
## of 32-bit floating-point numbers.
@ -179,3 +180,11 @@ template maximumPositiveValue*(T : typedesc[float64]) : float64 = DBL_MAX
template epsilon*(T : typedesc[float64]): float64 = DBL_EPSILON
## The difference between 1.0 and the smallest number greater than
## 1.0 that can be represented in a 64-bit floating-point type.
when isMainModule:
func is_significant(x: float): bool =
if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true
else: false
doAssert is_significant(10.0)

View file

@ -1,200 +1,4 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements experimental features which may soon be moved to
## the system module (or other more appropriate modules).
{.deprecated: "Use the new 'sugar' module instead".}
import macros
proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
#echo treeRepr(p)
#echo treeRepr(b)
result = newNimNode(nnkProcTy)
var formalParams = newNimNode(nnkFormalParams)
formalParams.add b
case p.kind
of nnkPar:
for i in 0 ..< p.len:
let ident = p[i]
var identDefs = newNimNode(nnkIdentDefs)
case ident.kind
of nnkExprColonExpr:
identDefs.add ident[0]
identDefs.add ident[1]
else:
identDefs.add newIdentNode("i" & $i)
identDefs.add(ident)
identDefs.add newEmptyNode()
formalParams.add identDefs
else:
var identDefs = newNimNode(nnkIdentDefs)
identDefs.add newIdentNode("i0")
identDefs.add(p)
identDefs.add newEmptyNode()
formalParams.add identDefs
result.add formalParams
result.add newEmptyNode()
#echo(treeRepr(result))
#echo(result.toStrLit())
macro `=>`*(p, b: untyped): untyped =
## Syntax sugar for anonymous procedures.
##
## .. code-block:: nim
##
## proc passTwoAndTwo(f: (int, int) -> int): int =
## f(2, 2)
##
## passTwoAndTwo((x, y) => x + y) # 4
#echo treeRepr(p)
#echo(treeRepr(b))
var params: seq[NimNode] = @[newIdentNode("auto")]
case p.kind
of nnkPar:
for c in children(p):
var identDefs = newNimNode(nnkIdentDefs)
case c.kind
of nnkExprColonExpr:
identDefs.add(c[0])
identDefs.add(c[1])
identDefs.add(newEmptyNode())
of nnkIdent:
identDefs.add(c)
identDefs.add(newIdentNode("auto"))
identDefs.add(newEmptyNode())
of nnkInfix:
if c[0].kind == nnkIdent and c[0].ident == !"->":
var procTy = createProcType(c[1], c[2])
params[0] = procTy[0][0]
for i in 1 ..< procTy[0].len:
params.add(procTy[0][i])
else:
error("Expected proc type (->) got (" & $c[0].ident & ").")
break
else:
echo treeRepr c
error("Incorrect procedure parameter list.")
params.add(identDefs)
of nnkIdent:
var identDefs = newNimNode(nnkIdentDefs)
identDefs.add(p)
identDefs.add(newIdentNode("auto"))
identDefs.add(newEmptyNode())
params.add(identDefs)
of nnkInfix:
if p[0].kind == nnkIdent and p[0].ident == !"->":
var procTy = createProcType(p[1], p[2])
params[0] = procTy[0][0]
for i in 1 ..< procTy[0].len:
params.add(procTy[0][i])
else:
error("Expected proc type (->) got (" & $p[0].ident & ").")
else:
error("Incorrect procedure parameter list.")
result = newProc(params = params, body = b, procType = nnkLambda)
#echo(result.treeRepr)
#echo(result.toStrLit())
#return result # TODO: Bug?
macro `->`*(p, b: untyped): untyped =
## Syntax sugar for procedure types.
##
## .. code-block:: nim
##
## proc pass2(f: (float, float) -> float): float =
## f(2, 2)
##
## # is the same as:
##
## proc pass2(f: proc (x, y: float): float): float =
## f(2, 2)
result = createProcType(p, b)
type ListComprehension = object
var lc*: ListComprehension
macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped =
## List comprehension, returns a sequence. `comp` is the actual list
## comprehension, for example ``x | (x <- 1..10, x mod 2 == 0)``. `typ` is
## the type that will be stored inside the result seq.
##
## .. code-block:: nim
##
## echo lc[x | (x <- 1..10, x mod 2 == 0), int]
##
## const n = 20
## echo lc[(x,y,z) | (x <- 1..n, y <- x..n, z <- y..n, x*x + y*y == z*z),
## tuple[a,b,c: int]]
expectLen(comp, 3)
expectKind(comp, nnkInfix)
expectKind(comp[0], nnkIdent)
assert($comp[0].ident == "|")
result = newCall(
newDotExpr(
newIdentNode("result"),
newIdentNode("add")),
comp[1])
for i in countdown(comp[2].len-1, 0):
let x = comp[2][i]
expectMinLen(x, 1)
if x[0].kind == nnkIdent and $x[0].ident == "<-":
expectLen(x, 3)
result = newNimNode(nnkForStmt).add(x[1], x[2], result)
else:
result = newIfStmt((x, result))
result = newNimNode(nnkCall).add(
newNimNode(nnkPar).add(
newNimNode(nnkLambda).add(
newEmptyNode(),
newEmptyNode(),
newEmptyNode(),
newNimNode(nnkFormalParams).add(
newNimNode(nnkBracketExpr).add(
newIdentNode("seq"),
typ)),
newEmptyNode(),
newEmptyNode(),
newStmtList(
newAssignment(
newIdentNode("result"),
newNimNode(nnkPrefix).add(
newIdentNode("@"),
newNimNode(nnkBracket))),
result))))
macro dump*(x: typed): untyped =
## Dumps the content of an expression, useful for debugging.
## It accepts any expression and prints a textual representation
## of the tree representing the expression - as it would appear in
## source code - together with the value of the expression.
##
## As an example,
##
## .. code-block:: nim
## let
## x = 10
## y = 20
## dump(x + y)
##
## will print ``x + y = 30``.
let s = x.toStrLit
let r = quote do:
debugEcho `s`, " = ", `x`
return r
include sugar

View file

@ -45,7 +45,6 @@ type
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``
## operator instead of ``mod`` for truncation of the hash value.
{.deprecated: [THash: Hash].}
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
## mixes a hash value `h` with `val` to produce a new hash value. This is

View file

@ -31,14 +31,23 @@ import
macros, strutils
const
coreAttr* = " id class title style "
eventAttr* = " onclick ondblclick onmousedown onmouseup " &
"onmouseover onmousemove onmouseout onkeypress onkeydown onkeyup onload "
commonAttr* = coreAttr & eventAttr
coreAttr* = " accesskey class contenteditable dir hidden id lang " &
"spellcheck style tabindex title translate "
eventAttr* = "onabort onblur oncancel oncanplay oncanplaythrough onchange " &
"onclick oncuechange ondblclick ondurationchange onemptied onended " &
"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload " &
"onloadeddata onloadedmetadata onloadstart onmousedown onmouseenter " &
"onmouseleave onmousemove onmouseout onmouseover onmouseup onmousewheel " &
"onpause onplay onplaying onprogress onratechange onreset onresize " &
"onscroll onseeked onseeking onselect onshow onstalled onsubmit " &
"onsuspend ontimeupdate ontoggle onvolumechange onwaiting "
ariaAttr* = " role "
commonAttr* = coreAttr & eventAttr & ariaAttr
proc getIdent(e: NimNode): string {.compileTime.} =
case e.kind
of nnkIdent: result = normalize($e.ident)
of nnkIdent:
result = e.strVal.normalize
of nnkAccQuoted:
result = getIdent(e[0])
for i in 1 .. e.len-1:
@ -92,19 +101,21 @@ proc xmlCheckedTag*(e: NimNode, tag: string, optAttr = "", reqAttr = "",
result.add(newStrLitNode("</"))
result.add(newStrLitNode(tag))
result.add(newStrLitNode(">"))
result = nestList(!"&", result)
when compiles(nestList(ident"&", result)):
result = nestList(ident"&", result)
else:
result = nestList(!"&", result)
macro a*(e: varargs[untyped]): untyped =
## generates the HTML ``a`` element.
let e = callsite()
result = xmlCheckedTag(e, "a", "href charset type hreflang rel rev " &
"accesskey tabindex" & commonAttr)
result = xmlCheckedTag(e, "a", "href target download rel hreflang type " &
commonAttr)
macro acronym*(e: varargs[untyped]): untyped =
## generates the HTML ``acronym`` element.
macro abbr*(e: varargs[untyped]): untyped =
## generates the HTML ``abbr`` element.
let e = callsite()
result = xmlCheckedTag(e, "acronym", commonAttr)
result = xmlCheckedTag(e, "abbr", commonAttr)
macro address*(e: varargs[untyped]): untyped =
## generates the HTML ``address`` element.
@ -114,8 +125,24 @@ macro address*(e: varargs[untyped]): untyped =
macro area*(e: varargs[untyped]): untyped =
## generates the HTML ``area`` element.
let e = callsite()
result = xmlCheckedTag(e, "area", "shape coords href nohref" &
" accesskey tabindex" & commonAttr, "alt", true)
result = xmlCheckedTag(e, "area", "coords download href hreflang rel " &
"shape target type" & commonAttr, "alt", true)
macro article*(e: varargs[untyped]): untyped =
## generates the HTML ``article`` element.
let e = callsite()
result = xmlCheckedTag(e, "article", commonAttr)
macro aside*(e: varargs[untyped]): untyped =
## generates the HTML ``aside`` element.
let e = callsite()
result = xmlCheckedTag(e, "aside", commonAttr)
macro audio*(e: varargs[untyped]): untyped =
## generates the HTML ``audio`` element.
let e = callsite()
result = xmlCheckedTag(e, "audio", "src crossorigin preload " &
"autoplay mediagroup loop muted controls" & commonAttr)
macro b*(e: varargs[untyped]): untyped =
## generates the HTML ``b`` element.
@ -125,7 +152,17 @@ macro b*(e: varargs[untyped]): untyped =
macro base*(e: varargs[untyped]): untyped =
## generates the HTML ``base`` element.
let e = callsite()
result = xmlCheckedTag(e, "base", "", "href", true)
result = xmlCheckedTag(e, "base", "href target" & commonAttr, "", true)
macro bdi*(e: varargs[untyped]): untyped =
## generates the HTML ``bdi`` element.
let e = callsite()
result = xmlCheckedTag(e, "bdi", commonAttr)
macro bdo*(e: varargs[untyped]): untyped =
## generates the HTML ``bdo`` element.
let e = callsite()
result = xmlCheckedTag(e, "bdo", commonAttr)
macro big*(e: varargs[untyped]): untyped =
## generates the HTML ``big`` element.
@ -140,18 +177,26 @@ macro blockquote*(e: varargs[untyped]): untyped =
macro body*(e: varargs[untyped]): untyped =
## generates the HTML ``body`` element.
let e = callsite()
result = xmlCheckedTag(e, "body", commonAttr)
result = xmlCheckedTag(e, "body", "onafterprint onbeforeprint " &
"onbeforeunload onhashchange onmessage onoffline ononline onpagehide " &
"onpageshow onpopstate onstorage onunload" & commonAttr)
macro br*(e: varargs[untyped]): untyped =
## generates the HTML ``br`` element.
let e = callsite()
result = xmlCheckedTag(e, "br", "", "", true)
result = xmlCheckedTag(e, "br", commonAttr, "", true)
macro button*(e: varargs[untyped]): untyped =
## generates the HTML ``button`` element.
let e = callsite()
result = xmlCheckedTag(e, "button", "accesskey tabindex " &
"disabled name type value" & commonAttr)
result = xmlCheckedTag(e, "button", "autofocus disabled form formaction " &
"formenctype formmethod formnovalidate formtarget menu name type value" &
commonAttr)
macro canvas*(e: varargs[untyped]): untyped =
## generates the HTML ``canvas`` element.
let e = callsite()
result = xmlCheckedTag(e, "canvas", "width height" & commonAttr)
macro caption*(e: varargs[untyped]): untyped =
## generates the HTML ``caption`` element.
@ -171,12 +216,22 @@ macro code*(e: varargs[untyped]): untyped =
macro col*(e: varargs[untyped]): untyped =
## generates the HTML ``col`` element.
let e = callsite()
result = xmlCheckedTag(e, "col", "span align valign" & commonAttr, "", true)
result = xmlCheckedTag(e, "col", "span" & commonAttr, "", true)
macro colgroup*(e: varargs[untyped]): untyped =
## generates the HTML ``colgroup`` element.
let e = callsite()
result = xmlCheckedTag(e, "colgroup", "span align valign" & commonAttr)
result = xmlCheckedTag(e, "colgroup", "span" & commonAttr)
macro data*(e: varargs[untyped]): untyped =
## generates the HTML ``data`` element.
let e = callsite()
result = xmlCheckedTag(e, "data", "value" & commonAttr)
macro datalist*(e: varargs[untyped]): untyped =
## generates the HTML ``datalist`` element.
let e = callsite()
result = xmlCheckedTag(e, "datalist", commonAttr)
macro dd*(e: varargs[untyped]): untyped =
## generates the HTML ``dd`` element.
@ -213,16 +268,37 @@ macro em*(e: varargs[untyped]): untyped =
let e = callsite()
result = xmlCheckedTag(e, "em", commonAttr)
macro embed*(e: varargs[untyped]): untyped =
## generates the HTML ``embed`` element.
let e = callsite()
result = xmlCheckedTag(e, "embed", "src type height width" &
commonAttr, "", true)
macro fieldset*(e: varargs[untyped]): untyped =
## generates the HTML ``fieldset`` element.
let e = callsite()
result = xmlCheckedTag(e, "fieldset", commonAttr)
result = xmlCheckedTag(e, "fieldset", "disabled form name" & commonAttr)
macro figure*(e: varargs[untyped]): untyped =
## generates the HTML ``figure`` element.
let e = callsite()
result = xmlCheckedTag(e, "figure", commonAttr)
macro figcaption*(e: varargs[untyped]): untyped =
## generates the HTML ``figcaption`` element.
let e = callsite()
result = xmlCheckedTag(e, "figcaption", commonAttr)
macro footer*(e: varargs[untyped]): untyped =
## generates the HTML ``footer`` element.
let e = callsite()
result = xmlCheckedTag(e, "footer", commonAttr)
macro form*(e: varargs[untyped]): untyped =
## generates the HTML ``form`` element.
let e = callsite()
result = xmlCheckedTag(e, "form", "method encype accept accept-charset" &
commonAttr, "action")
result = xmlCheckedTag(e, "form", "accept-charset action autocomplete " &
"enctype method name novalidate target" & commonAttr)
macro h1*(e: varargs[untyped]): untyped =
## generates the HTML ``h1`` element.
@ -257,7 +333,12 @@ macro h6*(e: varargs[untyped]): untyped =
macro head*(e: varargs[untyped]): untyped =
## generates the HTML ``head`` element.
let e = callsite()
result = xmlCheckedTag(e, "head", "profile")
result = xmlCheckedTag(e, "head", commonAttr)
macro header*(e: varargs[untyped]): untyped =
## generates the HTML ``header`` element.
let e = callsite()
result = xmlCheckedTag(e, "header", commonAttr)
macro html*(e: varargs[untyped]): untyped =
## generates the HTML ``html`` element.
@ -274,16 +355,26 @@ macro i*(e: varargs[untyped]): untyped =
let e = callsite()
result = xmlCheckedTag(e, "i", commonAttr)
macro iframe*(e: varargs[untyped]): untyped =
## generates the HTML ``iframe`` element.
let e = callsite()
result = xmlCheckedTag(e, "iframe", "src srcdoc name sandbox width height" &
commonAttr)
macro img*(e: varargs[untyped]): untyped =
## generates the HTML ``img`` element.
let e = callsite()
result = xmlCheckedTag(e, "img", "longdesc height width", "src alt", true)
result = xmlCheckedTag(e, "img", "crossorigin usemap ismap height width" &
commonAttr, "src alt", true)
macro input*(e: varargs[untyped]): untyped =
## generates the HTML ``input`` element.
let e = callsite()
result = xmlCheckedTag(e, "input", "name type value checked maxlength src" &
" alt accept disabled readonly accesskey tabindex" & commonAttr, "", true)
result = xmlCheckedTag(e, "input", "accept alt autocomplete autofocus " &
"checked dirname disabled form formaction formenctype formmethod " &
"formnovalidate formtarget height inputmode list max maxlength min " &
"minlength multiple name pattern placeholder readonly required size " &
"src step type value width" & commonAttr, "", true)
macro ins*(e: varargs[untyped]): untyped =
## generates the HTML ``ins`` element.
@ -295,36 +386,64 @@ macro kbd*(e: varargs[untyped]): untyped =
let e = callsite()
result = xmlCheckedTag(e, "kbd", commonAttr)
macro keygen*(e: varargs[untyped]): untyped =
## generates the HTML ``keygen`` element.
let e = callsite()
result = xmlCheckedTag(e, "keygen", "autofocus challenge disabled " &
"form keytype name" & commonAttr)
macro label*(e: varargs[untyped]): untyped =
## generates the HTML ``label`` element.
let e = callsite()
result = xmlCheckedTag(e, "label", "for accesskey" & commonAttr)
result = xmlCheckedTag(e, "label", "form for" & commonAttr)
macro legend*(e: varargs[untyped]): untyped =
## generates the HTML ``legend`` element.
let e = callsite()
result = xmlCheckedTag(e, "legend", "accesskey" & commonAttr)
result = xmlCheckedTag(e, "legend", commonAttr)
macro li*(e: varargs[untyped]): untyped =
## generates the HTML ``li`` element.
let e = callsite()
result = xmlCheckedTag(e, "li", commonAttr)
result = xmlCheckedTag(e, "li", "value" & commonAttr)
macro link*(e: varargs[untyped]): untyped =
## generates the HTML ``link`` element.
let e = callsite()
result = xmlCheckedTag(e, "link", "href charset hreflang type rel rev media" &
commonAttr, "", true)
result = xmlCheckedTag(e, "link", "href crossorigin rel media hreflang " &
"type sizes" & commonAttr, "", true)
macro main*(e: varargs[untyped]): untyped =
## generates the HTML ``main`` element.
let e = callsite()
result = xmlCheckedTag(e, "main", commonAttr)
macro map*(e: varargs[untyped]): untyped =
## generates the HTML ``map`` element.
let e = callsite()
result = xmlCheckedTag(e, "map", "class title" & eventAttr, "id", false)
result = xmlCheckedTag(e, "map", "name" & commonAttr)
macro mark*(e: varargs[untyped]): untyped =
## generates the HTML ``mark`` element.
let e = callsite()
result = xmlCheckedTag(e, "mark", commonAttr)
macro meta*(e: varargs[untyped]): untyped =
## generates the HTML ``meta`` element.
let e = callsite()
result = xmlCheckedTag(e, "meta", "name http-equiv scheme", "content", true)
result = xmlCheckedTag(e, "meta", "name http-equiv content charset" &
commonAttr, "", true)
macro meter*(e: varargs[untyped]): untyped =
## generates the HTML ``meter`` element.
let e = callsite()
result = xmlCheckedTag(e, "meter", "value min max low high optimum" &
commonAttr)
macro nav*(e: varargs[untyped]): untyped =
## generates the HTML ``nav`` element.
let e = callsite()
result = xmlCheckedTag(e, "nav", commonAttr)
macro noscript*(e: varargs[untyped]): untyped =
## generates the HTML ``noscript`` element.
@ -334,13 +453,13 @@ macro noscript*(e: varargs[untyped]): untyped =
macro `object`*(e: varargs[untyped]): untyped =
## generates the HTML ``object`` element.
let e = callsite()
result = xmlCheckedTag(e, "object", "classid data codebase declare type " &
"codetype archive standby width height name tabindex" & commonAttr)
result = xmlCheckedTag(e, "object", "data type typemustmatch name usemap " &
"form width height" & commonAttr)
macro ol*(e: varargs[untyped]): untyped =
## generates the HTML ``ol`` element.
let e = callsite()
result = xmlCheckedTag(e, "ol", commonAttr)
result = xmlCheckedTag(e, "ol", "reversed start type" & commonAttr)
macro optgroup*(e: varargs[untyped]): untyped =
## generates the HTML ``optgroup`` element.
@ -350,7 +469,13 @@ macro optgroup*(e: varargs[untyped]): untyped =
macro option*(e: varargs[untyped]): untyped =
## generates the HTML ``option`` element.
let e = callsite()
result = xmlCheckedTag(e, "option", "selected value" & commonAttr)
result = xmlCheckedTag(e, "option", "disabled label selected value" &
commonAttr)
macro output*(e: varargs[untyped]): untyped =
## generates the HTML ``output`` element.
let e = callsite()
result = xmlCheckedTag(e, "output", "for form name" & commonAttr)
macro p*(e: varargs[untyped]): untyped =
## generates the HTML ``p`` element.
@ -360,18 +485,53 @@ macro p*(e: varargs[untyped]): untyped =
macro param*(e: varargs[untyped]): untyped =
## generates the HTML ``param`` element.
let e = callsite()
result = xmlCheckedTag(e, "param", "value id type valuetype", "name", true)
result = xmlCheckedTag(e, "param", commonAttr, "name value", true)
macro pre*(e: varargs[untyped]): untyped =
## generates the HTML ``pre`` element.
let e = callsite()
result = xmlCheckedTag(e, "pre", commonAttr)
macro progress*(e: varargs[untyped]): untyped =
## generates the HTML ``progress`` element.
let e = callsite()
result = xmlCheckedTag(e, "progress", "value max" & commonAttr)
macro q*(e: varargs[untyped]): untyped =
## generates the HTML ``q`` element.
let e = callsite()
result = xmlCheckedTag(e, "q", "cite" & commonAttr)
macro rb*(e: varargs[untyped]): untyped =
## generates the HTML ``rb`` element.
let e = callsite()
result = xmlCheckedTag(e, "rb", commonAttr)
macro rp*(e: varargs[untyped]): untyped =
## generates the HTML ``rp`` element.
let e = callsite()
result = xmlCheckedTag(e, "rp", commonAttr)
macro rt*(e: varargs[untyped]): untyped =
## generates the HTML ``rt`` element.
let e = callsite()
result = xmlCheckedTag(e, "rt", commonAttr)
macro rtc*(e: varargs[untyped]): untyped =
## generates the HTML ``rtc`` element.
let e = callsite()
result = xmlCheckedTag(e, "rtc", commonAttr)
macro ruby*(e: varargs[untyped]): untyped =
## generates the HTML ``ruby`` element.
let e = callsite()
result = xmlCheckedTag(e, "ruby", commonAttr)
macro s*(e: varargs[untyped]): untyped =
## generates the HTML ``s`` element.
let e = callsite()
result = xmlCheckedTag(e, "s", commonAttr)
macro samp*(e: varargs[untyped]): untyped =
## generates the HTML ``samp`` element.
let e = callsite()
@ -380,19 +540,30 @@ macro samp*(e: varargs[untyped]): untyped =
macro script*(e: varargs[untyped]): untyped =
## generates the HTML ``script`` element.
let e = callsite()
result = xmlCheckedTag(e, "script", "src charset defer", "type", false)
result = xmlCheckedTag(e, "script", "src type charset async defer " &
"crossorigin" & commonAttr)
macro section*(e: varargs[untyped]): untyped =
## generates the HTML ``section`` element.
let e = callsite()
result = xmlCheckedTag(e, "section", commonAttr)
macro select*(e: varargs[untyped]): untyped =
## generates the HTML ``select`` element.
let e = callsite()
result = xmlCheckedTag(e, "select", "name size multiple disabled tabindex" &
commonAttr)
result = xmlCheckedTag(e, "select", "autofocus disabled form multiple " &
"name required size" & commonAttr)
macro small*(e: varargs[untyped]): untyped =
## generates the HTML ``small`` element.
let e = callsite()
result = xmlCheckedTag(e, "small", commonAttr)
macro source*(e: varargs[untyped]): untyped =
## generates the HTML ``source`` element.
let e = callsite()
result = xmlCheckedTag(e, "source", "type" & commonAttr, "src", true)
macro span*(e: varargs[untyped]): untyped =
## generates the HTML ``span`` element.
let e = callsite()
@ -406,7 +577,7 @@ macro strong*(e: varargs[untyped]): untyped =
macro style*(e: varargs[untyped]): untyped =
## generates the HTML ``style`` element.
let e = callsite()
result = xmlCheckedTag(e, "style", "media title", "type")
result = xmlCheckedTag(e, "style", "media type" & commonAttr)
macro sub*(e: varargs[untyped]): untyped =
## generates the HTML ``sub`` element.
@ -421,57 +592,77 @@ macro sup*(e: varargs[untyped]): untyped =
macro table*(e: varargs[untyped]): untyped =
## generates the HTML ``table`` element.
let e = callsite()
result = xmlCheckedTag(e, "table", "summary border cellpadding cellspacing" &
" frame rules width" & commonAttr)
result = xmlCheckedTag(e, "table", "border sortable" & commonAttr)
macro tbody*(e: varargs[untyped]): untyped =
## generates the HTML ``tbody`` element.
let e = callsite()
result = xmlCheckedTag(e, "tbody", "align valign" & commonAttr)
result = xmlCheckedTag(e, "tbody", commonAttr)
macro td*(e: varargs[untyped]): untyped =
## generates the HTML ``td`` element.
let e = callsite()
result = xmlCheckedTag(e, "td", "colspan rowspan abbr axis headers scope" &
" align valign" & commonAttr)
result = xmlCheckedTag(e, "td", "colspan rowspan headers" & commonAttr)
macro `template`*(e: varargs[untyped]): untyped =
## generates the HTML ``template`` element.
let e = callsite()
result = xmlCheckedTag(e, "template", commonAttr)
macro textarea*(e: varargs[untyped]): untyped =
## generates the HTML ``textarea`` element.
let e = callsite()
result = xmlCheckedTag(e, "textarea", " name disabled readonly accesskey" &
" tabindex" & commonAttr, "rows cols", false)
result = xmlCheckedTag(e, "textarea", "autocomplete autofocus cols " &
"dirname disabled form inputmode maxlength minlength name placeholder " &
"readonly required rows wrap" & commonAttr)
macro tfoot*(e: varargs[untyped]): untyped =
## generates the HTML ``tfoot`` element.
let e = callsite()
result = xmlCheckedTag(e, "tfoot", "align valign" & commonAttr)
result = xmlCheckedTag(e, "tfoot", commonAttr)
macro th*(e: varargs[untyped]): untyped =
## generates the HTML ``th`` element.
let e = callsite()
result = xmlCheckedTag(e, "th", "colspan rowspan abbr axis headers scope" &
" align valign" & commonAttr)
result = xmlCheckedTag(e, "th", "colspan rowspan headers abbr scope axis" &
" sorted" & commonAttr)
macro thead*(e: varargs[untyped]): untyped =
## generates the HTML ``thead`` element.
let e = callsite()
result = xmlCheckedTag(e, "thead", "align valign" & commonAttr)
result = xmlCheckedTag(e, "thead", commonAttr)
macro time*(e: varargs[untyped]): untyped =
## generates the HTML ``time`` element.
let e = callsite()
result = xmlCheckedTag(e, "time", "datetime" & commonAttr)
macro title*(e: varargs[untyped]): untyped =
## generates the HTML ``title`` element.
let e = callsite()
result = xmlCheckedTag(e, "title")
result = xmlCheckedTag(e, "title", commonAttr)
macro tr*(e: varargs[untyped]): untyped =
## generates the HTML ``tr`` element.
let e = callsite()
result = xmlCheckedTag(e, "tr", "align valign" & commonAttr)
result = xmlCheckedTag(e, "tr", commonAttr)
macro track*(e: varargs[untyped]): untyped =
## generates the HTML ``track`` element.
let e = callsite()
result = xmlCheckedTag(e, "track", "kind srclang label default" &
commonAttr, "src", true)
macro tt*(e: varargs[untyped]): untyped =
## generates the HTML ``tt`` element.
let e = callsite()
result = xmlCheckedTag(e, "tt", commonAttr)
macro u*(e: varargs[untyped]): untyped =
## generates the HTML ``u`` element.
let e = callsite()
result = xmlCheckedTag(e, "u", commonAttr)
macro ul*(e: varargs[untyped]): untyped =
## generates the HTML ``ul`` element.
let e = callsite()
@ -482,6 +673,17 @@ macro `var`*(e: varargs[untyped]): untyped =
let e = callsite()
result = xmlCheckedTag(e, "var", commonAttr)
macro video*(e: varargs[untyped]): untyped =
## generates the HTML ``video`` element.
let e = callsite()
result = xmlCheckedTag(e, "video", "src crossorigin poster preload " &
"autoplay mediagroup loop muted controls width height" & commonAttr)
macro wbr*(e: varargs[untyped]): untyped =
## generates the HTML ``wbr`` element.
let e = callsite()
result = xmlCheckedTag(e, "wbr", commonAttr, "", true)
when isMainModule:
let nim = "Nim"
assert h1(a(href="http://nim-lang.org", nim)) ==

File diff suppressed because it is too large Load diff

View file

@ -73,12 +73,18 @@
## progress of the HTTP request.
##
## .. code-block:: Nim
## var client = newAsyncHttpClient()
## import asyncdispatch, httpclient
##
## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
## echo("Downloaded ", progress, " of ", total)
## echo("Current rate: ", speed div 1000, "kb/s")
## client.onProgressChanged = onProgressChanged
## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
##
## proc asyncProc() {.async.} =
## var client = newAsyncHttpClient()
## client.onProgressChanged = onProgressChanged
## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
##
## waitFor asyncProc()
##
## If you would like to remove the callback simply set it to ``nil``.
##
@ -179,7 +185,7 @@ proc body*(response: Response): string =
## Retrieves the specified response's body.
##
## The response's body stream is read synchronously.
if response.body.isNil():
if response.body.len == 0:
response.body = response.bodyStream.readAll()
return response.body
@ -192,7 +198,7 @@ proc `body=`*(response: Response, value: string) {.deprecated.} =
proc body*(response: AsyncResponse): Future[string] {.async.} =
## Reads the response's body and caches it. The read is performed only
## once.
if response.body.isNil:
if response.body.len == 0:
response.body = await readAll(response.bodyStream)
return response.body
@ -213,10 +219,6 @@ type
## and ``postContent`` proc,
## when the server returns an error
{.deprecated: [TResponse: Response, PProxy: Proxy,
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
].}
const defUserAgent* = "Nim httpclient/" & NimVersion
proc httpError(msg: string) =
@ -241,7 +243,7 @@ proc parseChunks(s: Socket, timeout: int): string =
var i = 0
if chunkSizeStr == "":
httpError("Server terminated connection prematurely")
while true:
while i < chunkSizeStr.len:
case chunkSizeStr[i]
of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@ -249,8 +251,6 @@ proc parseChunks(s: Socket, timeout: int): string =
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions.
@ -358,21 +358,17 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
else:
result.body = ""
{.deprecated: [THttpMethod: HttpMethod].}
when not defined(ssl):
type SSLContext = ref object
var defaultSSLContext {.threadvar.}: SSLContext
when defined(ssl):
defaultSSLContext = newContext(verifyMode = CVerifyNone)
template contextOrDefault(ctx: SSLContext): SSLContext =
var result = ctx
if ctx == nil:
if defaultSSLContext == nil:
defaultSSLContext = newContext(verifyMode = CVerifyNone)
proc getDefaultSSL(): SSLContext =
result = defaultSslContext
when defined(ssl):
if result == nil:
defaultSSLContext = newContext(verifyMode = CVerifyNone)
result = defaultSSLContext
result
doAssert result != nil, "failure to initialize the SSL context"
proc newProxy*(url: string, auth = ""): Proxy =
## Constructs a new ``TProxy`` object.
@ -382,23 +378,23 @@ proc newMultipartData*: MultipartData =
## Constructs a new ``MultipartData`` object.
MultipartData(content: @[])
proc add*(p: var MultipartData, name, content: string, filename: string = nil,
contentType: string = nil) =
proc add*(p: var MultipartData, name, content: string, filename: string = "",
contentType: string = "") =
## Add a value to the multipart data. Raises a `ValueError` exception if
## `name`, `filename` or `contentType` contain newline characters.
if {'\c','\L'} in name:
raise newException(ValueError, "name contains a newline character")
if filename != nil and {'\c','\L'} in filename:
if {'\c','\L'} in filename:
raise newException(ValueError, "filename contains a newline character")
if contentType != nil and {'\c','\L'} in contentType:
if {'\c','\L'} in contentType:
raise newException(ValueError, "contentType contains a newline character")
var str = "Content-Disposition: form-data; name=\"" & name & "\""
if filename != nil:
if filename.len > 0:
str.add("; filename=\"" & filename & "\"")
str.add("\c\L")
if contentType != nil:
if contentType.len > 0:
str.add("Content-Type: " & contentType & "\c\L")
str.add("\c\L" & content & "\c\L")
@ -438,7 +434,7 @@ proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
var contentType: string
let (_, fName, ext) = splitFile(file)
if ext.len > 0:
contentType = m.getMimetype(ext[1..ext.high], nil)
contentType = m.getMimetype(ext[1..ext.high], "")
p.add(name, readFile(file), fName & ext, contentType)
result = p
@ -461,7 +457,7 @@ proc `[]=`*(p: var MultipartData, name: string,
p.add(name, file.content, file.name, file.contentType)
proc format(p: MultipartData): tuple[contentType, body: string] =
if p == nil or p.content == nil or p.content.len == 0:
if p == nil or p.content.len == 0:
return ("", "")
# Create boundary that is not in the data to be formatted
@ -482,9 +478,9 @@ proc format(p: MultipartData): tuple[contentType, body: string] =
result.body.add("--" & bound & "--\c\L")
proc request*(url: string, httpMethod: string, extraHeaders = "",
body = "", sslContext = defaultSSLContext, timeout = -1,
body = "", sslContext = getDefaultSSL(), timeout = -1,
userAgent = defUserAgent, proxy: Proxy = nil): Response
{.deprecated.} =
{.deprecated: "use HttpClient.request instead".} =
## | Requests ``url`` with the custom method string specified by the
## | ``httpMethod`` parameter.
## | Extra headers can be specified and must be separated by ``\c\L``
@ -581,8 +577,8 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
result = parseResponse(s, httpMethod != "HEAD", timeout)
proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
body = "", sslContext = defaultSSLContext, timeout = -1,
proc request*(url: string, httpMethod = HttpGET, extraHeaders = "",
body = "", sslContext = getDefaultSSL(), timeout = -1,
userAgent = defUserAgent, proxy: Proxy = nil): Response
{.deprecated.} =
## | Requests ``url`` with the specified ``httpMethod``.
@ -613,7 +609,7 @@ proc getNewLocation(lastURL: string, headers: HttpHeaders): string =
result = $parsed
proc get*(url: string, extraHeaders = "", maxRedirects = 5,
sslContext: SSLContext = defaultSSLContext,
sslContext: SSLContext = getDefaultSSL(),
timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil): Response {.deprecated.} =
## | GETs the ``url`` and returns a ``Response`` object
@ -622,19 +618,19 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5,
## | An optional timeout can be specified in milliseconds, if reading from the
## server takes longer than specified an ETimeout exception will be raised.
##
## ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead.
result = request(url, httpGET, extraHeaders, "", sslContext, timeout,
## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead.
result = request(url, HttpGET, extraHeaders, "", sslContext, timeout,
userAgent, proxy)
var lastURL = url
for i in 1..maxRedirects:
if result.status.redirection():
let redirectTo = getNewLocation(lastURL, result.headers)
result = request(redirectTo, httpGET, extraHeaders, "", sslContext,
result = request(redirectTo, HttpGET, extraHeaders, "", sslContext,
timeout, userAgent, proxy)
lastURL = redirectTo
proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
sslContext: SSLContext = defaultSSLContext,
sslContext: SSLContext = getDefaultSSL(),
timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil): string {.deprecated.} =
## | GETs the body and returns it as a string.
@ -653,7 +649,7 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
proc post*(url: string, extraHeaders = "", body = "",
maxRedirects = 5,
sslContext: SSLContext = defaultSSLContext,
sslContext: SSLContext = getDefaultSSL(),
timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil,
multipart: MultipartData = nil): Response {.deprecated.} =
@ -683,20 +679,20 @@ proc post*(url: string, extraHeaders = "", body = "",
if not multipart.isNil:
xh.add(withNewLine("Content-Type: " & mpContentType))
result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
result = request(url, HttpPOST, xh, xb, sslContext, timeout, userAgent,
proxy)
var lastURL = url
for i in 1..maxRedirects:
if result.status.redirection():
let redirectTo = getNewLocation(lastURL, result.headers)
var meth = if result.status != "307": httpGet else: httpPost
var meth = if result.status != "307": HttpGet else: HttpPost
result = request(redirectTo, meth, xh, xb, sslContext, timeout,
userAgent, proxy)
lastURL = redirectTo
proc postContent*(url: string, extraHeaders = "", body = "",
maxRedirects = 5,
sslContext: SSLContext = defaultSSLContext,
sslContext: SSLContext = getDefaultSSL(),
timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil,
multipart: MultipartData = nil): string
@ -719,7 +715,7 @@ proc postContent*(url: string, extraHeaders = "", body = "",
return r.body
proc downloadFile*(url: string, outputFilename: string,
sslContext: SSLContext = defaultSSLContext,
sslContext: SSLContext = getDefaultSSL(),
timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil) {.deprecated.} =
## | Downloads ``url`` and saves it to ``outputFilename``
@ -739,12 +735,13 @@ proc downloadFile*(url: string, outputFilename: string,
proc generateHeaders(requestUrl: Uri, httpMethod: string,
headers: HttpHeaders, body: string, proxy: Proxy): string =
# GET
result = httpMethod.toUpperAscii()
let upperMethod = httpMethod.toUpperAscii()
result = upperMethod
result.add ' '
if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"):
if proxy.isNil or requestUrl.scheme == "https":
# /path?query
if requestUrl.path[0] != '/': result.add '/'
if not requestUrl.path.startsWith("/"): result.add '/'
result.add(requestUrl.path)
if requestUrl.query.len > 0:
result.add("?" & requestUrl.query)
@ -768,7 +765,9 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
add(result, "Connection: Keep-Alive\c\L")
# Content length header.
if body.len > 0 and not headers.hasKey("Content-Length"):
const requiresBody = ["POST", "PUT", "PATCH"]
let needsContentLength = body.len > 0 or upperMethod in requiresBody
if needsContentLength and not headers.hasKey("Content-Length"):
add(result, "Content-Length: " & $body.len & "\c\L")
# Proxy auth header.
@ -808,6 +807,7 @@ type
lastProgressReport: float
when SocketType is AsyncSocket:
bodyStream: FutureStream[string]
parseBodyFut: Future[void]
else:
bodyStream: Stream
getBody: bool ## When `false`, the body is never read in requestAux.
@ -816,7 +816,7 @@ type
HttpClient* = HttpClientBase[Socket]
proc newHttpClient*(userAgent = defUserAgent,
maxRedirects = 5, sslContext = defaultSslContext, proxy: Proxy = nil,
maxRedirects = 5, sslContext = getDefaultSSL(), proxy: Proxy = nil,
timeout = -1): HttpClient =
## Creates a new HttpClient instance.
##
@ -843,7 +843,7 @@ proc newHttpClient*(userAgent = defUserAgent,
result.bodyStream = newStringStream()
result.getBody = true
when defined(ssl):
result.sslContext = contextOrDefault(sslContext)
result.sslContext = sslContext
type
AsyncHttpClient* = HttpClientBase[AsyncSocket]
@ -851,7 +851,7 @@ type
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
proc newAsyncHttpClient*(userAgent = defUserAgent,
maxRedirects = 5, sslContext = defaultSslContext,
maxRedirects = 5, sslContext = getDefaultSSL(),
proxy: Proxy = nil): AsyncHttpClient =
## Creates a new AsyncHttpClient instance.
##
@ -875,7 +875,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
result.bodyStream = newFutureStream[string]("newAsyncHttpClient")
result.getBody = true
when defined(ssl):
result.sslContext = contextOrDefault(sslContext)
result.sslContext = sslContext
proc close*(client: HttpClient | AsyncHttpClient) =
## Closes any connections held by the HTTP client.
@ -929,7 +929,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
var i = 0
if chunkSizeStr == "":
httpError("Server terminated connection prematurely")
while true:
while i < chunkSizeStr.len:
case chunkSizeStr[i]
of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@ -937,8 +937,6 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions.
@ -1069,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
if getBody:
when client is HttpClient:
client.bodyStream = newStringStream()
result.bodyStream = client.bodyStream
parseBody(client, result.headers, result.version)
else:
client.bodyStream = newFutureStream[string]("parseResponse")
await parseBody(client, result.headers, result.version)
result.bodyStream = client.bodyStream
result.bodyStream = client.bodyStream
assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
client.parseBodyFut = parseBody(client, result.headers, result.version)
# do not wait here for the body request to complete
proc newConnection(client: HttpClient | AsyncHttpClient,
url: Uri) {.multisync.} =
@ -1162,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
# Helper that actually makes the request. Does not handle redirects.
let requestUrl = parseUri(url)
when client is AsyncHttpClient:
if not client.parseBodyFut.isNil:
# let the current operation finish before making another request
await client.parseBodyFut
client.parseBodyFut = nil
await newConnection(client, requestUrl)
let effectiveHeaders = client.headers.override(headers)
@ -1187,7 +1195,7 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
## Connects to the hostname specified by the URL and performs a request
## using the custom method string specified by ``httpMethod``.
##
## Connection will kept alive. Further requests on the same ``client`` to
## Connection will be kept alive. Further requests on the same ``client`` to
## the same hostname will not require a new connection to be made. The
## connection can be closed by using the ``close`` procedure.
##
@ -1199,7 +1207,7 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
for i in 1..client.maxRedirects:
if result.status.redirection():
let redirectTo = getNewLocation(lastURL, result.headers)
result = await client.request(redirectTo, httpMethod, body, headers)
result = await client.requestAux(redirectTo, httpMethod, body, headers)
lastURL = redirectTo

View file

@ -45,10 +45,6 @@ type
## TCP/IP tunnel, usually used for proxies.
HttpPatch ## Applies partial modifications to a resource.
{.deprecated: [httpGet: HttpGet, httpHead: HttpHead, httpPost: HttpPost,
httpPut: HttpPut, httpDelete: HttpDelete, httpTrace: HttpTrace,
httpOptions: HttpOptions, httpConnect: HttpConnect].}
const
Http100* = HttpCode(100)
@ -190,11 +186,11 @@ proc len*(headers: HttpHeaders): int = return headers.table.len
proc parseList(line: string, list: var seq[string], start: int): int =
var i = 0
var current = ""
while line[start + i] notin {'\c', '\l', '\0'}:
while start+i < line.len and line[start + i] notin {'\c', '\l'}:
i += line.skipWhitespace(start + i)
i += line.parseUntil(current, {'\c', '\l', ','}, start + i)
list.add(current)
if line[start + i] == ',':
if start+i < line.len and line[start + i] == ',':
i.inc # Skip ,
current.setLen(0)

View file

@ -110,7 +110,6 @@ when false:
# TODO: Fix this, or get rid of it.
type
RequestMethod = enum reqGet, reqPost
{.deprecated: [TRequestMethod: RequestMethod].}
proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
var env = newStringTable(modeCaseInsensitive)
@ -126,7 +125,7 @@ when false:
var dataAvail = false
while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect.
var L = toLower(buf.string)
var L = toLowerAscii(buf.string)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
@ -199,7 +198,7 @@ when false:
notFound(client)
else:
when defined(Windows):
var ext = splitFile(path).ext.toLower
var ext = splitFile(path).ext.toLowerAscii
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
@ -225,7 +224,6 @@ type
PAsyncHTTPServer* = ref AsyncHTTPServer
AsyncHTTPServer = object of Server
asyncSocket: AsyncSocket
{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
proc open*(s: var Server, port = Port(80), reuseAddr = false) =
## creates a new server at port `port`. If ``port == 0`` a free port is
@ -303,7 +301,7 @@ proc next*(s: var Server) =
if s.reqMethod == "POST":
# Check for Expect header
if s.headers.hasKey("Expect"):
if s.headers["Expect"].toLower == "100-continue":
if s.headers["Expect"].toLowerAscii == "100-continue":
s.client.sendStatus("100 Continue")
else:
s.client.sendStatus("417 Expectation Failed")
@ -427,7 +425,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
if s.reqMethod == "POST":
# Check for Expect header
if s.headers.hasKey("Expect"):
if s.headers["Expect"].toLower == "100-continue":
if s.headers["Expect"].toLowerAscii == "100-continue":
s.client.sendStatus("100 Continue")
else:
s.client.sendStatus("417 Expectation Failed")

View file

@ -18,13 +18,13 @@ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD {.deprecated.} =
protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD
{.deprecated.} =
{.deprecated: "use createAsyncNativeSocket instead".} =
createAsyncNativeSocketImpl(domain, sockType, protocol)
when defined(windows) or defined(nimdoc):

View file

@ -12,50 +12,6 @@ when not defined(nimscript):
when defined(windows):
import winlean
proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
## Retrieves the operating system's error flag, ``errno``.
## On Windows ``GetLastError`` is checked before ``errno``.
## Returns "" if no error occurred.
##
## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.
result = ""
when defined(Windows) and not defined(nimscript):
var err = getLastError()
if err != 0'i32:
when useWinUnicode:
var msgbuf: WideCString
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf
if msgbuf != nil: localFree(cast[pointer](msgbuf))
else:
var msgbuf: cstring
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf
if msgbuf != nil: localFree(msgbuf)
when not defined(nimscript):
if errno != 0'i32:
result = $c_strerror(errno)
{.push warning[deprecated]: off.}
proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
deprecated.} =
## raises an OSError exception with the given message ``msg``.
## If ``msg == ""``, the operating system's error flag
## (``errno``) is converted to a readable error message. On Windows
## ``GetLastError`` is checked before ``errno``.
## If no error flag is set, the message ``unknown OS error`` is used.
##
## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
if len(msg) == 0:
var m = osErrorMsg()
raise newException(OSError, if m.len > 0: m else: "unknown OS error")
else:
raise newException(OSError, msg)
{.pop.}
proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
proc `$`*(err: OSErrorCode): string {.borrow.}
@ -104,13 +60,13 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
if additionalInfo.len == 0:
e.msg = osErrorMsg(errorCode)
else:
e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
e.msg = osErrorMsg(errorCode) & "\nAdditional info: '" & additionalInfo & "'"
if e.msg == "":
e.msg = "unknown OS error"
raise e
{.push stackTrace:off.}
proc osLastError*(): OSErrorCode =
proc osLastError*(): OSErrorCode {.sideEffect.} =
## Retrieves the last operating system error code.
##
## This procedure is useful in the event when an OS call fails. In that case

View file

@ -48,16 +48,6 @@ when not defined(android):
proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
{.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
header: "<sys/resource.h>".}: cint
type
RLimit {.importc: "struct rlimit",
header: "<sys/resource.h>", pure, final.} = object
rlim_cur: int
rlim_max: int
proc getrlimit(resource: cint, rlp: var RLimit): cint
{.importc: "getrlimit",header: "<sys/resource.h>".}
when hasThreadSupport:
type
SelectorImpl[T] = object
@ -82,7 +72,7 @@ type
proc newSelector*[T](): Selector[T] =
# Retrieve the maximum fd count (for current OS) via getrlimit()
var a = RLimit()
if getrlimit(RLIMIT_NOFILE, a) != 0:
if getrlimit(posix.RLIMIT_NOFILE, a) != 0:
raiseOsError(osLastError())
var maxFD = int(a.rlim_max)
doAssert(maxFD > 0)
@ -102,6 +92,9 @@ proc newSelector*[T](): Selector[T] =
result.maxFD = maxFD
result.fds = newSeq[SelectorKey[T]](maxFD)
for i in 0 ..< maxFD:
result.fds[i].ident = InvalidIdent
proc close*[T](s: Selector[T]) =
let res = posix.close(s.epollFD)
when hasThreadSupport:
@ -110,12 +103,6 @@ proc close*[T](s: Selector[T]) =
if res != 0:
raiseIOSelectorsError(osLastError())
template clearKey[T](key: ptr SelectorKey[T]) =
var empty: T
key.ident = 0
key.events = {}
key.data = empty
proc newSelectEvent*(): SelectEvent =
let fdci = eventfd(0, 0)
if fdci == -1:
@ -145,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) =
let fdi = int(fd)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0, "Descriptor $# already registered" % $fdi)
doAssert(s.fds[fdi].ident == InvalidIdent, "Descriptor $# already registered" % $fdi)
s.setKey(fdi, events, 0, data)
if events != {}:
var epv = EpollEvent(events: EPOLLRDHUP)
@ -162,7 +149,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0,
doAssert(pkey.ident != InvalidIdent,
"Descriptor $# is not registered in the selector!" % $fdi)
doAssert(pkey.events * maskEvents == {})
if pkey.events != events:
@ -190,7 +177,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0,
doAssert(pkey.ident != InvalidIdent,
"Descriptor $# is not registered in the selector!" % $fdi)
if pkey.events != {}:
when not defined(android):
@ -253,7 +240,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.efd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, "Event is not registered in the queue!")
doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
doAssert(Event.User in pkey.events)
var epv = EpollEvent()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
@ -272,7 +259,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
setNonBlocking(fdi.cint)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
var events = {Event.Timer}
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
@ -317,7 +304,7 @@ when not defined(android):
setNonBlocking(fdi.cint)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint
@ -344,7 +331,7 @@ when not defined(android):
setNonBlocking(fdi.cint)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint
@ -357,7 +344,7 @@ when not defined(android):
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
let fdi = int(ev.efd)
doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
s.setKey(fdi, {Event.User}, 0, data)
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = ev.efd.uint
@ -391,7 +378,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
let fdi = int(resTable[i].data.u64)
let pevents = resTable[i].events
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(pkey.ident != InvalidIdent)
var rkey = ReadyKey(fd: fdi, events: {})
if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
@ -492,7 +479,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
return s.fds[fd.int].ident != 0
return s.fds[fd.int].ident != InvalidIdent
proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd)
@ -528,4 +515,4 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body2
proc getFd*[T](s: Selector[T]): int =
return s.epollFd.int
return s.epollFd.int

View file

@ -114,6 +114,9 @@ proc newSelector*[T](): Selector[T] =
result.fds = newSeq[SelectorKey[T]](maxFD)
result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS)
for i in 0 ..< maxFD:
result.fds[i].ident = InvalidIdent
result.sock = usock
result.kqFD = kqFD
result.maxFD = maxFD.int
@ -128,12 +131,6 @@ proc close*[T](s: Selector[T]) =
if res1 != 0 or res2 != 0:
raiseIOSelectorsError(osLastError())
template clearKey[T](key: ptr SelectorKey[T]) =
var empty: T
key.ident = 0
key.events = {}
key.data = empty
proc newSelectEvent*(): SelectEvent =
var fds: array[2, cint]
if posix.pipe(fds) != 0:
@ -221,7 +218,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) =
let fdi = int(fd)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
s.setKey(fdi, events, 0, data)
if events != {}:
@ -242,7 +239,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0,
doAssert(pkey.ident != InvalidIdent,
"Descriptor $# is not registered in the queue!" % $fdi)
doAssert(pkey.events * maskEvents == {})
@ -269,7 +266,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
data: T): int {.discardable.} =
let fdi = getUnique(s)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD
@ -291,7 +288,7 @@ proc registerSignal*[T](s: Selector[T], signal: int,
data: T): int {.discardable.} =
let fdi = getUnique(s)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
s.setKey(fdi, {Event.Signal}, signal, data)
var nmask, omask: Sigset
@ -315,7 +312,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
data: T): int {.discardable.} =
let fdi = getUnique(s)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
var kflags: cushort = EV_ONESHOT or EV_ADD
setKey(s, fdi, {Event.Process, Event.Oneshot}, pid, data)
@ -331,7 +328,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
let fdi = ev.rfd.int
doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
setKey(s, fdi, {Event.User}, 0, data)
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
@ -374,7 +371,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0,
doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & $fdi & "] is not registered in the queue!")
if pkey.events != {}:
@ -434,7 +431,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.rfd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, "Event is not registered in the queue!")
doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
doAssert(Event.User in pkey.events)
modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil)
when not declared(CACHE_EVENTS):
@ -570,8 +567,11 @@ proc selectInto*[T](s: Selector[T], timeout: int,
doAssert(true, "Unsupported kqueue filter in the queue!")
if (kevent.flags and EV_EOF) != 0:
# TODO this error handling needs to be rethought.
# `fflags` can sometimes be `0x80000000` and thus we use 'cast'
# here:
if kevent.fflags != 0:
rkey.errorCode = kevent.fflags.OSErrorCode
rkey.errorCode = cast[OSErrorCode](kevent.fflags)
else:
# This assumes we are dealing with sockets.
# TODO: For future-proofing it might be a good idea to give the
@ -593,7 +593,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
return s.fds[fd.int].ident != 0
return s.fds[fd.int].ident != InvalidIdent
proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd)

View file

@ -40,16 +40,6 @@ type
wfd: cint
SelectEvent* = ptr SelectEventImpl
var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
header: "<sys/resource.h>".}: cint
type
rlimit {.importc: "struct rlimit",
header: "<sys/resource.h>", pure, final.} = object
rlim_cur: int
rlim_max: int
proc getrlimit(resource: cint, rlp: var rlimit): cint
{.importc: "getrlimit",header: "<sys/resource.h>".}
when hasThreadSupport:
template withPollLock[T](s: Selector[T], body: untyped) =
acquire(s.lock)
@ -63,8 +53,8 @@ else:
body
proc newSelector*[T](): Selector[T] =
var a = rlimit()
if getrlimit(RLIMIT_NOFILE, a) != 0:
var a = RLimit()
if getrlimit(posix.RLIMIT_NOFILE, a) != 0:
raiseIOSelectorsError(osLastError())
var maxFD = int(a.rlim_max)
@ -80,6 +70,9 @@ proc newSelector*[T](): Selector[T] =
result.fds = newSeq[SelectorKey[T]](maxFD)
result.pollfds = newSeq[TPollFd](maxFD)
for i in 0 ..< maxFD:
result.fds[i].ident = InvalidIdent
proc close*[T](s: Selector[T]) =
when hasThreadSupport:
deinitLock(s.lock)
@ -87,12 +80,6 @@ proc close*[T](s: Selector[T]) =
deallocSharedArray(s.pollfds)
deallocShared(cast[pointer](s))
template clearKey[T](key: ptr SelectorKey[T]) =
var empty: T
key.ident = 0
key.events = {}
key.data = empty
template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
withPollLock(s):
var pollev: cshort = 0
@ -145,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) =
var fdi = int(fd)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
doAssert(s.fds[fdi].ident == InvalidIdent)
setKey(s, fdi, events, 0, data)
if events != {}: s.pollAdd(fdi.cint, events)
@ -156,7 +143,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0,
doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & $fdi & "] is not registered in the queue!")
doAssert(pkey.events * maskEvents == {})
@ -172,7 +159,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
var fdi = int(ev.rfd)
doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
var events = {Event.User}
setKey(s, fdi, events, 0, data)
events.incl(Event.Read)
@ -182,9 +169,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0,
doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & $fdi & "] is not registered in the queue!")
pkey.ident = 0
pkey.ident = InvalidIdent
pkey.events = {}
s.pollRemove(fdi.cint)
@ -192,9 +179,9 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.rfd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, "Event is not registered in the queue!")
doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
doAssert(Event.User in pkey.events)
pkey.ident = 0
pkey.ident = InvalidIdent
pkey.events = {}
s.pollRemove(fdi.cint)
@ -280,7 +267,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
return s.fds[fd.int].ident != 0
return s.fds[fd.int].ident != InvalidIdent
proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd)
@ -317,4 +304,4 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
proc getFd*[T](s: Selector[T]): int =
return -1
return -1

View file

@ -99,6 +99,9 @@ proc newSelector*[T](): Selector[T] =
result = Selector[T]()
result.fds = newSeq[SelectorKey[T]](FD_SETSIZE)
for i in 0 ..< FD_SETSIZE:
result.fds[i].ident = InvalidIdent
IOFD_ZERO(addr result.rSet)
IOFD_ZERO(addr result.wSet)
IOFD_ZERO(addr result.eSet)
@ -195,7 +198,7 @@ proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event],
var i = 0
let fdi = int(fd)
while i < FD_SETSIZE:
if s.fds[i].ident == 0:
if s.fds[i].ident == InvalidIdent:
var pkey = addr(s.fds[i])
pkey.ident = fdi
pkey.events = events
@ -221,7 +224,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) =
var i = 0
while i < FD_SETSIZE:
if s.fds[i].ident == fd.int:
s.fds[i].ident = 0
s.fds[i].ident = InvalidIdent
s.fds[i].events = {}
s.fds[i].data = empty
break
@ -307,7 +310,10 @@ proc selectInto*[T](s: Selector[T], timeout: int,
var rset, wset, eset: FdSet
if timeout != -1:
tv.tv_sec = timeout.int32 div 1_000
when defined(genode):
tv.tv_sec = Time(timeout div 1_000)
else:
tv.tv_sec = timeout.int32 div 1_000
tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
else:
ptv = nil
@ -335,7 +341,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
var k = 0
while (i < FD_SETSIZE) and (k < count):
if s.fds[i].ident != 0:
if s.fds[i].ident != InvalidIdent:
var flag = false
var pkey = addr(s.fds[i])
var rkey = ReadyKey(fd: int(pkey.ident), events: {})
@ -388,7 +394,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
for i in 0..<FD_SETSIZE:
if s.fds[i].ident == fdi:
return true
inc(i)
when hasThreadSupport:
template withSelectLock[T](s: Selector[T], body: untyped) =

View file

@ -89,509 +89,21 @@
## echo j2
import
hashes, tables, strutils, lexbase, streams, unicode, macros
hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson
export
tables.`$`
export
parsejson.JsonEventKind, parsejson.JsonError, JsonParser, JsonKindError,
open, close, str, getInt, getFloat, kind, getColumn, getLine, getFilename,
errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr
when defined(nimJsonGet):
{.pragma: deprecatedGet, deprecated.}
else:
{.pragma: deprecatedGet.}
type
JsonEventKind* = enum ## enumeration of all events that may occur when parsing
jsonError, ## an error occurred during parsing
jsonEof, ## end of file reached
jsonString, ## a string literal
jsonInt, ## an integer literal
jsonFloat, ## a float literal
jsonTrue, ## the value ``true``
jsonFalse, ## the value ``false``
jsonNull, ## the value ``null``
jsonObjectStart, ## start of an object: the ``{`` token
jsonObjectEnd, ## end of an object: the ``}`` token
jsonArrayStart, ## start of an array: the ``[`` token
jsonArrayEnd ## start of an array: the ``]`` token
TokKind = enum # must be synchronized with TJsonEventKind!
tkError,
tkEof,
tkString,
tkInt,
tkFloat,
tkTrue,
tkFalse,
tkNull,
tkCurlyLe,
tkCurlyRi,
tkBracketLe,
tkBracketRi,
tkColon,
tkComma
JsonError* = enum ## enumeration that lists all errors that can occur
errNone, ## no error
errInvalidToken, ## invalid token
errStringExpected, ## string expected
errColonExpected, ## ``:`` expected
errCommaExpected, ## ``,`` expected
errBracketRiExpected, ## ``]`` expected
errCurlyRiExpected, ## ``}`` expected
errQuoteExpected, ## ``"`` or ``'`` expected
errEOC_Expected, ## ``*/`` expected
errEofExpected, ## EOF expected
errExprExpected ## expr expected
ParserState = enum
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
stateExpectObjectComma, stateExpectColon, stateExpectValue
JsonParser* = object of BaseLexer ## the parser object.
a: string
tok: TokKind
kind: JsonEventKind
err: JsonError
state: seq[ParserState]
filename: string
JsonKindError* = object of ValueError ## raised by the ``to`` macro if the
## JSON kind is incorrect.
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
TJsonParser: JsonParser, TTokKind: TokKind].}
const
errorMessages: array[JsonError, string] = [
"no error",
"invalid token",
"string expected",
"':' expected",
"',' expected",
"']' expected",
"'}' expected",
"'\"' or \"'\" expected",
"'*/' expected",
"EOF expected",
"expression expected"
]
tokToStr: array[TokKind, string] = [
"invalid token",
"EOF",
"string literal",
"int literal",
"float literal",
"true",
"false",
"null",
"{", "}", "[", "]", ":", ","
]
proc open*(my: var JsonParser, input: Stream, filename: string) =
## initializes the parser with an input stream. `Filename` is only used
## for nice error messages.
lexbase.open(my, input)
my.filename = filename
my.state = @[stateStart]
my.kind = jsonError
my.a = ""
proc close*(my: var JsonParser) {.inline.} =
## closes the parser `my` and its associated input stream.
lexbase.close(my)
proc str*(my: JsonParser): string {.inline.} =
## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
## ``jsonString``
assert(my.kind in {jsonInt, jsonFloat, jsonString})
return my.a
proc getInt*(my: JsonParser): BiggestInt {.inline.} =
## returns the number for the event: ``jsonInt``
assert(my.kind == jsonInt)
return parseBiggestInt(my.a)
proc getFloat*(my: JsonParser): float {.inline.} =
## returns the number for the event: ``jsonFloat``
assert(my.kind == jsonFloat)
return parseFloat(my.a)
proc kind*(my: JsonParser): JsonEventKind {.inline.} =
## returns the current event type for the JSON parser
return my.kind
proc getColumn*(my: JsonParser): int {.inline.} =
## get the current column the parser has arrived at.
result = getColNumber(my, my.bufpos)
proc getLine*(my: JsonParser): int {.inline.} =
## get the current line the parser has arrived at.
result = my.lineNumber
proc getFilename*(my: JsonParser): string {.inline.} =
## get the filename of the file that the parser processes.
result = my.filename
proc errorMsg*(my: JsonParser): string =
## returns a helpful error message for the event ``jsonError``
assert(my.kind == jsonError)
result = "$1($2, $3) Error: $4" % [
my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
proc errorMsgExpected*(my: JsonParser, e: string): string =
## returns an error message "`e` expected" in the same format as the
## other error messages
result = "$1($2, $3) Error: $4" % [
my.filename, $getLine(my), $getColumn(my), e & " expected"]
proc handleHexChar(c: char, x: var int): bool =
result = true # Success
case c
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
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
proc parseEscapedUTF16(buf: cstring, pos: var int): int =
result = 0
#UTF-16 escape is always 4 bytes.
for _ in 0..3:
if handleHexChar(buf[pos], result):
inc(pos)
else:
return -1
proc parseString(my: var JsonParser): TokKind =
result = tkString
var pos = my.bufpos + 1
var buf = my.buf
while true:
case buf[pos]
of '\0':
my.err = errQuoteExpected
result = tkError
break
of '"':
inc(pos)
break
of '\\':
case buf[pos+1]
of '\\', '"', '\'', '/':
add(my.a, buf[pos+1])
inc(pos, 2)
of 'b':
add(my.a, '\b')
inc(pos, 2)
of 'f':
add(my.a, '\f')
inc(pos, 2)
of 'n':
add(my.a, '\L')
inc(pos, 2)
of 'r':
add(my.a, '\C')
inc(pos, 2)
of 't':
add(my.a, '\t')
inc(pos, 2)
of 'u':
inc(pos, 2)
var r = parseEscapedUTF16(buf, pos)
if r < 0:
my.err = errInvalidToken
break
# Deal with surrogates
if (r and 0xfc00) == 0xd800:
if buf[pos] & buf[pos+1] != "\\u":
my.err = errInvalidToken
break
inc(pos, 2)
var s = parseEscapedUTF16(buf, pos)
if (s and 0xfc00) == 0xdc00 and s > 0:
r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00))
else:
my.err = errInvalidToken
break
add(my.a, toUTF8(Rune(r)))
else:
# don't bother with the error
add(my.a, buf[pos])
inc(pos)
of '\c':
pos = lexbase.handleCR(my, pos)
buf = my.buf
add(my.a, '\c')
of '\L':
pos = lexbase.handleLF(my, pos)
buf = my.buf
add(my.a, '\L')
else:
add(my.a, buf[pos])
inc(pos)
my.bufpos = pos # store back
proc skip(my: var JsonParser) =
var pos = my.bufpos
var buf = my.buf
while true:
case buf[pos]
of '/':
if buf[pos+1] == '/':
# skip line comment:
inc(pos, 2)
while true:
case buf[pos]
of '\0':
break
of '\c':
pos = lexbase.handleCR(my, pos)
buf = my.buf
break
of '\L':
pos = lexbase.handleLF(my, pos)
buf = my.buf
break
else:
inc(pos)
elif buf[pos+1] == '*':
# skip long comment:
inc(pos, 2)
while true:
case buf[pos]
of '\0':
my.err = errEOC_Expected
break
of '\c':
pos = lexbase.handleCR(my, pos)
buf = my.buf
of '\L':
pos = lexbase.handleLF(my, pos)
buf = my.buf
of '*':
inc(pos)
if buf[pos] == '/':
inc(pos)
break
else:
inc(pos)
else:
break
of ' ', '\t':
inc(pos)
of '\c':
pos = lexbase.handleCR(my, pos)
buf = my.buf
of '\L':
pos = lexbase.handleLF(my, pos)
buf = my.buf
else:
break
my.bufpos = pos
proc parseNumber(my: var JsonParser) =
var pos = my.bufpos
var buf = my.buf
if buf[pos] == '-':
add(my.a, '-')
inc(pos)
if buf[pos] == '.':
add(my.a, "0.")
inc(pos)
else:
while buf[pos] in Digits:
add(my.a, buf[pos])
inc(pos)
if buf[pos] == '.':
add(my.a, '.')
inc(pos)
# digits after the dot:
while buf[pos] in Digits:
add(my.a, buf[pos])
inc(pos)
if buf[pos] in {'E', 'e'}:
add(my.a, buf[pos])
inc(pos)
if buf[pos] in {'+', '-'}:
add(my.a, buf[pos])
inc(pos)
while buf[pos] in Digits:
add(my.a, buf[pos])
inc(pos)
my.bufpos = pos
proc parseName(my: var JsonParser) =
var pos = my.bufpos
var buf = my.buf
if buf[pos] in IdentStartChars:
while buf[pos] in IdentChars:
add(my.a, buf[pos])
inc(pos)
my.bufpos = pos
proc getTok(my: var JsonParser): TokKind =
setLen(my.a, 0)
skip(my) # skip whitespace, comments
case my.buf[my.bufpos]
of '-', '.', '0'..'9':
parseNumber(my)
if {'.', 'e', 'E'} in my.a:
result = tkFloat
else:
result = tkInt
of '"':
result = parseString(my)
of '[':
inc(my.bufpos)
result = tkBracketLe
of '{':
inc(my.bufpos)
result = tkCurlyLe
of ']':
inc(my.bufpos)
result = tkBracketRi
of '}':
inc(my.bufpos)
result = tkCurlyRi
of ',':
inc(my.bufpos)
result = tkComma
of ':':
inc(my.bufpos)
result = tkColon
of '\0':
result = tkEof
of 'a'..'z', 'A'..'Z', '_':
parseName(my)
case my.a
of "null": result = tkNull
of "true": result = tkTrue
of "false": result = tkFalse
else: result = tkError
else:
inc(my.bufpos)
result = tkError
my.tok = result
proc next*(my: var JsonParser) =
## retrieves the first/next event. This controls the parser.
var tk = getTok(my)
var i = my.state.len-1
# the following code is a state machine. If we had proper coroutines,
# the code could be much simpler.
case my.state[i]
of stateEof:
if tk == tkEof:
my.kind = jsonEof
else:
my.kind = jsonError
my.err = errEofExpected
of stateStart:
# tokens allowed?
case tk
of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
my.state[i] = stateEof # expect EOF next!
my.kind = JsonEventKind(ord(tk))
of tkBracketLe:
my.state.add(stateArray) # we expect any
my.kind = jsonArrayStart
of tkCurlyLe:
my.state.add(stateObject)
my.kind = jsonObjectStart
of tkEof:
my.kind = jsonEof
else:
my.kind = jsonError
my.err = errEofExpected
of stateObject:
case tk
of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
my.state.add(stateExpectColon)
my.kind = JsonEventKind(ord(tk))
of tkBracketLe:
my.state.add(stateExpectColon)
my.state.add(stateArray)
my.kind = jsonArrayStart
of tkCurlyLe:
my.state.add(stateExpectColon)
my.state.add(stateObject)
my.kind = jsonObjectStart
of tkCurlyRi:
my.kind = jsonObjectEnd
discard my.state.pop()
else:
my.kind = jsonError
my.err = errCurlyRiExpected
of stateArray:
case tk
of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
my.state.add(stateExpectArrayComma) # expect value next!
my.kind = JsonEventKind(ord(tk))
of tkBracketLe:
my.state.add(stateExpectArrayComma)
my.state.add(stateArray)
my.kind = jsonArrayStart
of tkCurlyLe:
my.state.add(stateExpectArrayComma)
my.state.add(stateObject)
my.kind = jsonObjectStart
of tkBracketRi:
my.kind = jsonArrayEnd
discard my.state.pop()
else:
my.kind = jsonError
my.err = errBracketRiExpected
of stateExpectArrayComma:
case tk
of tkComma:
discard my.state.pop()
next(my)
of tkBracketRi:
my.kind = jsonArrayEnd
discard my.state.pop() # pop stateExpectArrayComma
discard my.state.pop() # pop stateArray
else:
my.kind = jsonError
my.err = errBracketRiExpected
of stateExpectObjectComma:
case tk
of tkComma:
discard my.state.pop()
next(my)
of tkCurlyRi:
my.kind = jsonObjectEnd
discard my.state.pop() # pop stateExpectObjectComma
discard my.state.pop() # pop stateObject
else:
my.kind = jsonError
my.err = errCurlyRiExpected
of stateExpectColon:
case tk
of tkColon:
my.state[i] = stateExpectValue
next(my)
else:
my.kind = jsonError
my.err = errColonExpected
of stateExpectValue:
case tk
of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
my.state[i] = stateExpectObjectComma
my.kind = JsonEventKind(ord(tk))
of tkBracketLe:
my.state[i] = stateExpectObjectComma
my.state.add(stateArray)
my.kind = jsonArrayStart
of tkCurlyLe:
my.state[i] = stateExpectObjectComma
my.state.add(stateObject)
my.kind = jsonObjectStart
else:
my.kind = jsonError
my.err = errExprExpected
# ------------- higher level interface ---------------------------------------
type
JsonNodeKind* = enum ## possible JSON node types
JNull,
@ -620,15 +132,6 @@ type
of JArray:
elems*: seq[JsonNode]
JsonParsingError* = object of ValueError ## is raised for a JSON error
{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj,
PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].}
proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} =
## raises an `EJsonParsingError` exception.
raise newException(JsonParsingError, errorMsgExpected(p, msg))
proc newJString*(s: string): JsonNode =
## Creates a new `JString JsonNode`.
new(result)
@ -695,8 +198,8 @@ proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
if n.isNil or n.kind != JInt: return default
else: return n.num
proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} =
## Deprecated - use getInt or getBiggestInt instead
proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated: "use getInt or getBiggestInt instead".} =
## **Deprecated since v0.18.2:** use ``getInt`` or ``getBiggestInt`` instead.
getBiggestInt(n, default)
proc getFloat*(n: JsonNode, default: float = 0.0): float =
@ -709,8 +212,8 @@ proc getFloat*(n: JsonNode, default: float = 0.0): float =
of JInt: return float(n.num)
else: return default
proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} =
## Deprecated - use getFloat instead
proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated: "use getFloat instead".} =
## **Deprecated since v0.18.2:** use ``getFloat`` instead.
getFloat(n, default)
proc getBool*(n: JsonNode, default: bool = false): bool =
@ -720,8 +223,8 @@ proc getBool*(n: JsonNode, default: bool = false): bool =
if n.isNil or n.kind != JBool: return default
else: return n.bval
proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
## Deprecated - use getBVal instead
proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated: "use getBool instead".} =
## **Deprecated since v0.18.2:** use ``getBool`` instead.
getBool(n, default)
proc getFields*(n: JsonNode,
@ -734,7 +237,7 @@ proc getFields*(n: JsonNode,
else: return n.fields
proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] =
## Retrieves the int value of a `JArray JsonNode`.
## Retrieves the array of a `JArray JsonNode`.
##
## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil.
if n.isNil or n.kind != JArray: return default
@ -753,7 +256,6 @@ proc add*(obj: JsonNode, key: string, val: JsonNode) =
proc `%`*(s: string): JsonNode =
## Generic constructor for JSON data. Creates a new `JString JsonNode`.
new(result)
if s.isNil: return
result.kind = JString
result.str = s
@ -825,21 +327,24 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
result = newNimNode(nnkBracket)
for i in 0 ..< x.len:
result.add(toJson(x[i]))
result = newCall(bindSym"%", result)
result = newCall(bindSym("%", brOpen), result)
of nnkTableConstr: # object
if x.len == 0: return newCall(bindSym"newJObject")
result = newNimNode(nnkTableConstr)
for i in 0 ..< x.len:
x[i].expectKind nnkExprColonExpr
result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
result = newCall(bindSym"%", result)
result = newCall(bindSym("%", brOpen), result)
of nnkCurly: # empty object
x.expectLen(0)
result = newCall(bindSym"newJObject")
of nnkNilLit:
result = newCall(bindSym"newJNull")
of nnkPar:
if x.len == 1: result = toJson(x[0])
else: result = newCall(bindSym("%", brOpen), x)
else:
result = newCall(bindSym"%", x)
result = newCall(bindSym("%", brOpen), x)
macro `%*`*(x: untyped): untyped =
## Convert an expression to a JsonNode directly, without having to specify
@ -946,8 +451,8 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
assert(node.kind == JArray)
find(node.elems, val) >= 0
proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
## Deprecated for `hasKey`
proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use hasKey instead".} = node.hasKey(key)
## **Deprecated:** use `hasKey` instead.
proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
## Sets a field from a `JObject`.
@ -958,12 +463,29 @@ proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
## Traverses the node and gets the given value. If any of the
## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the
## intermediate data structures is not an object.
##
## This proc can be used to create tree structures on the
## fly (sometimes called `autovivification`:idx:):
##
## .. code-block:: nim
## myjson{"parent", "child", "grandchild"} = newJInt(1)
##
result = node
for key in keys:
if isNil(result) or result.kind != JObject:
return nil
result = result.fields.getOrDefault(key)
proc `{}`*(node: JsonNode, index: varargs[int]): JsonNode =
## Traverses the node and gets the given value. If any of the
## indexes do not exist, returns ``nil``. Also returns ``nil`` if one of the
## intermediate data structures is not an array.
result = node
for i in index:
if isNil(result) or result.kind != JArray or i >= node.len:
return nil
result = result.elems[i]
proc getOrDefault*(node: JsonNode, key: string): JsonNode =
## Gets a field from a `node`. If `node` is nil or not an object or
## value at `key` does not exist, returns nil
@ -986,7 +508,7 @@ proc delete*(obj: JsonNode, key: string) =
## Deletes ``obj[key]``.
assert(obj.kind == JObject)
if not obj.fields.hasKey(key):
raise newException(IndexError, "key not in object")
raise newException(KeyError, "key not in object")
obj.fields.del(key)
proc copy*(p: JsonNode): JsonNode =
@ -1023,10 +545,9 @@ proc newIndent(curr, indent: int, ml: bool): int =
proc nl(s: var string, ml: bool) =
s.add(if ml: "\n" else: " ")
proc escapeJson*(s: string; result: var string) =
## Converts a string `s` to its JSON representation.
proc escapeJsonUnquoted*(s: string; result: var string) =
## Converts a string `s` to its JSON representation without quotes.
## Appends to ``result``.
result.add("\"")
for c in s:
case c
of '\L': result.add("\\n")
@ -1035,12 +556,25 @@ proc escapeJson*(s: string; result: var string) =
of '\t': result.add("\\t")
of '\r': result.add("\\r")
of '"': result.add("\\\"")
of '\0'..'\7': result.add("\\u000" & $ord(c))
of '\14'..'\31': result.add("\\u00" & $ord(c))
of '\\': result.add("\\\\")
else: result.add(c)
proc escapeJsonUnquoted*(s: string): string =
## Converts a string `s` to its JSON representation without quotes.
result = newStringOfCap(s.len + s.len shr 3)
escapeJsonUnquoted(s, result)
proc escapeJson*(s: string; result: var string) =
## Converts a string `s` to its JSON representation with quotes.
## Appends to ``result``.
result.add("\"")
escapeJsonUnquoted(s, result)
result.add("\"")
proc escapeJson*(s: string): string =
## Converts a string `s` to its JSON representation.
## Converts a string `s` to its JSON representation with quotes.
result = newStringOfCap(s.len + s.len shr 3)
escapeJson(s, result)
@ -1180,10 +714,6 @@ iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] =
for key, val in mpairs(node.fields):
yield (key, val)
proc eat(p: var JsonParser, tok: TokKind) =
if p.tok == tok: discard getTok(p)
else: raiseParseErr(p, tokToStr[tok])
proc parseJson(p: var JsonParser): JsonNode =
## Parses JSON from a JSON Parser `p`.
case p.tok
@ -1239,10 +769,12 @@ when not defined(js):
## If `s` contains extra data, it will raise `JsonParsingError`.
var p: JsonParser
p.open(s, filename)
defer: p.close()
discard getTok(p) # read first token
result = p.parseJson()
eat(p, tkEof) # check if there is no extra data
try:
discard getTok(p) # read first token
result = p.parseJson()
eat(p, tkEof) # check if there is no extra data
finally:
p.close()
proc parseJson*(buffer: string): JsonNode =
## Parses JSON from `buffer`.
@ -1260,7 +792,6 @@ else:
from math import `mod`
type
JSObject = object
{.deprecated: [TJSObject: JSObject].}
proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
@ -1482,6 +1013,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
exprColonExpr.add(ifStmt)
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
proc detectDistinctType(typeSym: NimNode): NimNode =
let
typeImpl = getTypeImpl(typeSym)
typeInst = getTypeInst(typeSym)
result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
## Process a field from a ``RecList``.
##
@ -1500,8 +1038,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
# Add the field value.
# -> jsonNode["`field`"]
let indexedJsonNode = createJsonIndexer(jsonNode, $field)
exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode))
let typeNode = detectDistinctType(field)
exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
of nnkRecCase:
# A "case" field that introduces a variant.
let exprColonExpr = newNimNode(nnkExprColonExpr)
@ -1615,7 +1153,7 @@ proc processType(typeName: NimNode, obj: NimNode,
result = quote do:
(
verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
if `jsonNode`.kind == JNull: nil else: `jsonNode`.str
if `jsonNode`.kind == JNull: "" else: `jsonNode`.str
)
of "biggestint":
result = quote do:
@ -1687,7 +1225,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
result = quote do:
(
if `lenientJsonNode`.isNil: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
if `lenientJsonNode`.isNil or `jsonNode`.kind == JNull: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
)
of "table", "orderedtable":
let tableKeyType = typeSym[1]
@ -1726,7 +1264,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
let seqT = typeSym[1]
let forLoopI = genSym(nskForVar, "i")
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
let constructorNode = createConstructor(seqT, indexerNode)
let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
# Create a statement expression containing a for loop.
result = quote do:
@ -1762,17 +1300,35 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
# Handle all other types.
let obj = getType(typeSym)
if obj.kind == nnkBracketExpr:
let typeNode = getTypeImpl(typeSym)
if typeNode.typeKind == ntyDistinct:
result = createConstructor(typeNode, jsonNode)
elif obj.kind == nnkBracketExpr:
# When `Sym "Foo"` turns out to be a `ref object`.
result = createConstructor(obj, jsonNode)
else:
result = processType(typeSym, obj, jsonNode, false)
of nnkTupleTy:
result = processType(typeSym, typeSym, jsonNode, false)
of nnkPar:
of nnkPar, nnkTupleConstr:
# TODO: The fact that `jsonNode` here works to give a good line number
# is weird. Specifying typeSym should work but doesn't.
error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
of nnkDistinctTy:
var baseType = typeSym
# solve nested distinct types
while baseType.typeKind == ntyDistinct:
let impl = getTypeImpl(baseType[0])
if impl.typeKind != ntyDistinct:
baseType = baseType[0]
break
baseType = impl
let ret = createConstructor(baseType, jsonNode)
let typeInst = getTypeInst(typeSym)
result = quote do:
(
`typeInst`(`ret`)
)
else:
doAssert false, "Unable to create constructor for: " & $typeSym.kind
@ -1896,7 +1452,7 @@ macro to*(node: JsonNode, T: typedesc): untyped =
## doAssert data.person.age == 21
## doAssert data.list == @[1, 2, 3, 4]
let typeNode = getTypeInst(T)
let typeNode = getTypeImpl(T)
expectKind(typeNode, nnkBracketExpr)
doAssert(($typeNode[0]).normalize == "typedesc")
@ -1974,22 +1530,23 @@ when isMainModule:
doAssert parsedAgain["abc"].num == 5
# Bounds checking
try:
let a = testJson["a"][9]
doAssert(false, "EInvalidIndex not thrown")
except IndexError:
discard
try:
let a = testJson["a"][-1]
doAssert(false, "EInvalidIndex not thrown")
except IndexError:
discard
try:
doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value")
except:
doAssert(false, "EInvalidIndex thrown for valid index")
when compileOption("boundChecks"):
try:
let a = testJson["a"][9]
doAssert(false, "IndexError not thrown")
except IndexError:
discard
try:
let a = testJson["a"][-1]
doAssert(false, "IndexError not thrown")
except IndexError:
discard
try:
doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value")
except:
doAssert(false, "IndexError thrown for valid index")
doAssert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}")
doAssert(testJson{"b"}.getStr()=="asd", "Couldn't fetch a singly nested key with {}")
doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
@ -2060,7 +1617,10 @@ when isMainModule:
var parsed2 = parseFile("tests/testdata/jsontest2.json")
doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ"
doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0020" # for #7887
doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0020\"" # for #7887
# Test with extra data
when not defined(js):
@ -2079,5 +1639,3 @@ when isMainModule:
# bug #6438
doAssert($ %*[] == "[]")
doAssert($ %*{} == "{}")
echo("Tests succeeded!")

View file

@ -26,33 +26,33 @@
import typetraits
proc `+`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
proc `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
F(i) + f
proc `+`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
proc `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
f + F(i)
proc `-`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
proc `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
F(i) - f
proc `-`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
proc `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
f - F(i)
proc `*`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
proc `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
F(i) * f
proc `*`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
proc `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
f * F(i)
proc `/`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
proc `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
F(i) / f
proc `/`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
proc `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
f / F(i)
proc `<`*[I: SomeInteger, F: SomeReal](i: I, f: F): bool {.noSideEffect, inline.} =
proc `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} =
F(i) < f
proc `<`*[I: SomeInteger, F: SomeReal](f: F, i: I): bool {.noSideEffect, inline.} =
proc `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} =
f < F(i)
proc `<=`*[I: SomeInteger, F: SomeReal](i: I, f: F): bool {.noSideEffect, inline.} =
proc `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} =
F(i) <= f
proc `<=`*[I: SomeInteger, F: SomeReal](f: F, i: I): bool {.noSideEffect, inline.} =
proc `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} =
f <= F(i)
# Note that we must not defined `>=` and `>`, because system.nim already has a

View file

@ -40,8 +40,6 @@ type
offsetBase*: int # use ``offsetBase + bufpos`` to get the offset
refillChars: set[char]
{.deprecated: [TBaseLexer: BaseLexer].}
const
chrSize = sizeof(char)

View file

@ -96,10 +96,6 @@ when not defined(js):
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: [PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger].}
var
level {.threadvar.}: Level ## global log filter
handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
@ -107,14 +103,9 @@ var
proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string =
## Format a log message using the ``frmt`` format string, ``level`` and varargs.
## See the module documentation for the format string syntax.
const nilString = "nil"
var msgLen = 0
for arg in args:
if arg.isNil:
msgLen += nilString.len
else:
msgLen += arg.len
msgLen += arg.len
result = newStringOfCap(frmt.len + msgLen + 20)
var i = 0
while i < frmt.len:
@ -126,7 +117,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
var v = ""
let app = when defined(js): "" else: getAppFilename()
while frmt[i] in IdentChars:
v.add(toLower(frmt[i]))
v.add(toLowerAscii(frmt[i]))
inc(i)
case v
of "date": result.add(getDateStr())
@ -141,10 +132,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
of "levelname": result.add(LevelNames[level])
else: discard
for arg in args:
if arg.isNil:
result.add(nilString)
else:
result.add(arg)
result.add(arg)
method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
raises: [Exception], gcsafe,
@ -342,7 +330,6 @@ template fatal*(args: varargs[string, `$`]) =
proc addHandler*(handler: Logger) =
## Adds ``handler`` to the list of handlers.
if handlers.isNil: handlers = @[]
handlers.add(handler)
proc getHandlers*(): seq[Logger] =

View file

@ -98,8 +98,7 @@ proc storeAny(s: Stream, a: Any, stored: var IntSet) =
of akProc, akPointer, akCString: s.write($a.getPointer.ptrToInt)
of akString:
var x = getString(a)
if isNil(x): s.write("null")
elif x.validateUtf8() == -1: s.write(escapeJson(x))
if x.validateUtf8() == -1: s.write(escapeJson(x))
else:
s.write("[")
var i = 0
@ -270,6 +269,8 @@ proc store*[T](s: Stream, data: T) =
proc `$$`*[T](x: T): string =
## returns a string representation of `x`.
##
## Note: to serialize `x` to JSON use $(%x) from the ``json`` module
var stored = initIntSet()
var d: T
shallowCopy(d, x)
@ -279,6 +280,17 @@ proc `$$`*[T](x: T): string =
proc to*[T](data: string): T =
## reads data and transforms it to a ``T``.
runnableExamples:
type
Foo = object
id: int
bar: string
let x = Foo(id: 1, bar: "baz")
# serialize
let y = ($$x)
# deserialize back to type 'Foo':
let z = y.to[:Foo]
var tab = initTable[BiggestInt, pointer]()
loadAny(newStringStream(data), toAny(result), tab)
@ -309,7 +321,6 @@ when not defined(testing) and isMainModule:
Node = object
next, prev: PNode
data: string
{.deprecated: [TNode: Node].}
proc buildList(): PNode =
new(result)

View file

@ -12,7 +12,7 @@
## **Warning:** This module is deprecated since version 0.14.0.
{.deprecated.}
{.deadCodeElim: on.}
{.deadCodeElim: on.} # dce option deprecated
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
@ -29,21 +29,21 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
if s[i] notin chars or s[i] == '.': return false
while s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false
if i >= s.len or s[i] notin chars or s[i] == '.': return false
while i < s.len and s[i] in chars:
if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false
inc(i)
if s[i] != '@': return false
if i >= s.len or s[i] != '@': return false
var j = len(s)-1
if s[j] notin Letters: return false
if j >= 0 and s[j] notin Letters: return false
while j >= i and s[j] in Letters: dec(j)
inc(i) # skip '@'
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': return false
while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if i != s.len: return false
var x = substr(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
case toLower(x)
case toLowerAscii(x)
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true
else: return false

View file

@ -21,6 +21,8 @@ include "system/inclrtl"
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
import bitops
proc binom*(n, k: int): int {.noSideEffect.} =
## Computes the binomial coefficient
if k <= 0: return 1
@ -29,15 +31,25 @@ proc binom*(n, k: int): int {.noSideEffect.} =
for i in countup(2, k):
result = (result * (n + 1 - i)) div i
proc fac*(n: int): int {.noSideEffect.} =
proc createFactTable[N: static[int]]: array[N, int] =
result[0] = 1
for i in 1 ..< N:
result[i] = result[i - 1] * i
proc fac*(n: int): int =
## Computes the faculty/factorial function.
result = 1
for i in countup(2, n):
result = result * i
const factTable =
when sizeof(int) == 4:
createFactTable[13]()
else:
createFactTable[21]()
assert(n >= 0, $n & " must not be negative.")
assert(n < factTable.len, $n & " is too large to look up in the table")
factTable[n]
{.push checks:off, line_dir:off, stack_trace:off.}
when defined(Posix) and not defined(haiku):
when defined(Posix):
{.passl: "-lm".}
const
@ -117,8 +129,14 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
## If `x` is empty, 0 is returned.
for i in items(x): result = result + i
proc prod*[T](x: openArray[T]): T {.noSideEffect.} =
## Computes the product of the elements in ``x``.
## If ``x`` is empty, 1 is returned.
result = 1.T
for i in items(x): result = result * i
{.push noSideEffect.}
when not defined(JS):
when not defined(JS): # C
proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "<math.h>".}
## Computes the square root of `x`.
@ -129,15 +147,45 @@ when not defined(JS):
proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".}
## Computes the natural log of `x`
else: # JS
proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
proc log*[T: SomeFloat](x, base: T): T =
## Computes the logarithm ``base`` of ``x``
ln(x) / ln(base)
when not defined(JS): # C
proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
## Computes the common logarithm (base 10) of `x`
proc log2*[T: float32|float64](x: T): T = return ln(x) / ln(2.0)
## Computes the binary logarithm (base 2) of `x`
proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
## Computes the exponential function of `x` (pow(E, x))
proc sin*(x: float32): float32 {.importc: "sinf", header: "<math.h>".}
proc sin*(x: float64): float64 {.importc: "sin", header: "<math.h>".}
## Computes the sine of `x`
proc cos*(x: float32): float32 {.importc: "cosf", header: "<math.h>".}
proc cos*(x: float64): float64 {.importc: "cos", header: "<math.h>".}
## Computes the cosine of `x`
proc tan*(x: float32): float32 {.importc: "tanf", header: "<math.h>".}
proc tan*(x: float64): float64 {.importc: "tan", header: "<math.h>".}
## Computes the tangent of `x`
proc sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
proc sinh*(x: float64): float64 {.importc: "sinh", header: "<math.h>".}
## Computes the hyperbolic sine of `x`
proc cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
proc cosh*(x: float64): float64 {.importc: "cosh", header: "<math.h>".}
## Computes the hyperbolic cosine of `x`
proc tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
proc tanh*(x: float64): float64 {.importc: "tanh", header: "<math.h>".}
## Computes the hyperbolic tangent of `x`
proc arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".}
proc arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".}
## Computes the arc cosine of `x`
@ -154,52 +202,104 @@ when not defined(JS):
## results even when the resulting angle is near pi/2 or -pi/2
## (`x` near 0).
proc cos*(x: float32): float32 {.importc: "cosf", header: "<math.h>".}
proc cos*(x: float64): float64 {.importc: "cos", header: "<math.h>".}
## Computes the cosine of `x`
proc arcsinh*(x: float32): float32 {.importc: "asinhf", header: "<math.h>".}
proc arcsinh*(x: float64): float64 {.importc: "asinh", header: "<math.h>".}
## Computes the inverse hyperbolic sine of `x`
proc arccosh*(x: float32): float32 {.importc: "acoshf", header: "<math.h>".}
proc arccosh*(x: float64): float64 {.importc: "acosh", header: "<math.h>".}
## Computes the inverse hyperbolic cosine of `x`
proc arctanh*(x: float32): float32 {.importc: "atanhf", header: "<math.h>".}
proc arctanh*(x: float64): float64 {.importc: "atanh", header: "<math.h>".}
## Computes the inverse hyperbolic tangent of `x`
proc cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
proc cosh*(x: float64): float64 {.importc: "cosh", header: "<math.h>".}
## Computes the hyperbolic cosine of `x`
else: # JS
proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.}
proc log10*(x: float64): float64 {.importc: "Math.log10", nodecl.}
proc log2*(x: float32): float32 {.importc: "Math.log2", nodecl.}
proc log2*(x: float64): float64 {.importc: "Math.log2", nodecl.}
proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.}
proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.}
proc sin*[T: float32|float64](x: T): T {.importc: "Math.sin", nodecl.}
proc cos*[T: float32|float64](x: T): T {.importc: "Math.cos", nodecl.}
proc tan*[T: float32|float64](x: T): T {.importc: "Math.tan", nodecl.}
proc sinh*[T: float32|float64](x: T): T {.importc: "Math.sinh", nodecl.}
proc cosh*[T: float32|float64](x: T): T {.importc: "Math.cosh", nodecl.}
proc tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.}
proc arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.}
proc arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.}
proc arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.}
proc arctan2*[T: float32|float64](y, x: T): T {.importC: "Math.atan2", nodecl.}
proc arcsinh*[T: float32|float64](x: T): T {.importc: "Math.asinh", nodecl.}
proc arccosh*[T: float32|float64](x: T): T {.importc: "Math.acosh", nodecl.}
proc arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.}
proc cot*[T: float32|float64](x: T): T = 1.0 / tan(x)
## Computes the cotangent of `x`
proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x)
## Computes the secant of `x`.
proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x)
## Computes the cosecant of `x`
proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x)
## Computes the hyperbolic cotangent of `x`
proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x)
## Computes the hyperbolic secant of `x`
proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x)
## Computes the hyperbolic cosecant of `x`
proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x)
## Computes the inverse cotangent of `x`
proc arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x)
## Computes the inverse secant of `x`
proc arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x)
## Computes the inverse cosecant of `x`
proc arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x)
## Computes the inverse hyperbolic cotangent of `x`
proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x)
## Computes the inverse hyperbolic secant of `x`
proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
## Computes the inverse hyperbolic cosecant of `x`
const windowsCC89 = defined(windows) and defined(bcc)
when not defined(JS): # C
proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".}
## Computes the hypotenuse of a right-angle triangle with `x` and
## `y` as its base and height. Equivalent to ``sqrt(x*x + y*y)``.
proc sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
proc sinh*(x: float64): float64 {.importc: "sinh", header: "<math.h>".}
## Computes the hyperbolic sine of `x`
proc sin*(x: float32): float32 {.importc: "sinf", header: "<math.h>".}
proc sin*(x: float64): float64 {.importc: "sin", header: "<math.h>".}
## Computes the sine of `x`
proc tan*(x: float32): float32 {.importc: "tanf", header: "<math.h>".}
proc tan*(x: float64): float64 {.importc: "tan", header: "<math.h>".}
## Computes the tangent of `x`
proc tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
proc tanh*(x: float64): float64 {.importc: "tanh", header: "<math.h>".}
## Computes the hyperbolic tangent of `x`
proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
## computes x to power raised of y.
##
## To compute power between integers, use `^` e.g. 2 ^ 6
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
## The error function
proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
## The complementary error function
# TODO: add C89 version on windows
when not windowsCC89:
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
## The error function
proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
## The complementary error function
proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
## Natural log of the gamma function
proc tgamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
proc tgamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
## The gamma function
proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
## The gamma function
proc tgamma*(x: float32): float32
{.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
proc tgamma*(x: float64): float64
{.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
## The gamma function
## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
## Natural log of the gamma function
proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
@ -215,7 +315,7 @@ when not defined(JS):
## .. code-block:: nim
## echo ceil(-2.1) ## -2.0
when defined(windows) and (defined(vcc) or defined(bcc)):
when windowsCC89:
# MSVC 2010 don't have trunc/truncf
# this implementation was inspired by Go-lang Math.Trunc
proc truncImpl(f: float64): float64 =
@ -283,57 +383,31 @@ when not defined(JS):
## .. code-block:: nim
## echo trunc(PI) # 3.0
proc fmod*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
proc fmod*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
proc fmod*(x, y: float32): float32 {.deprecated, importc: "fmodf", header: "<math.h>".}
proc fmod*(x, y: float64): float64 {.deprecated, importc: "fmod", header: "<math.h>".}
## Computes the remainder of `x` divided by `y`
##
## .. code-block:: nim
## echo fmod(-2.5, 0.3) ## -0.1
else:
proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
## Computes the modulo operation for float operators.
else: # JS
proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y)
proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.}
proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.}
proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.}
proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.}
proc ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.}
proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
proc log10*[T: float32|float64](x: T): T = return ln(x) / ln(10.0)
proc log2*[T: float32|float64](x: T): T = return ln(x) / ln(2.0)
proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.}
proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.}
proc round0(x: float): float {.importc: "Math.round", nodecl.}
proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.}
proc arccos*(x: float32): float32 {.importc: "Math.acos", nodecl.}
proc arccos*(x: float64): float64 {.importc: "Math.acos", nodecl.}
proc arcsin*(x: float32): float32 {.importc: "Math.asin", nodecl.}
proc arcsin*(x: float64): float64 {.importc: "Math.asin", nodecl.}
proc arctan*(x: float32): float32 {.importc: "Math.atan", nodecl.}
proc arctan*(x: float64): float64 {.importc: "Math.atan", nodecl.}
proc arctan2*(y, x: float32): float32 {.importC: "Math.atan2", nodecl.}
proc arctan2*(y, x: float64): float64 {.importc: "Math.atan2", nodecl.}
proc cos*(x: float32): float32 {.importc: "Math.cos", nodecl.}
proc cos*(x: float64): float64 {.importc: "Math.cos", nodecl.}
proc cosh*(x: float32): float32 = return (exp(x)+exp(-x))*0.5
proc cosh*(x: float64): float64 = return (exp(x)+exp(-x))*0.5
proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y)
proc sinh*[T: float32|float64](x: T): T = return (exp(x)-exp(-x))*0.5
proc sin*(x: float32): float32 {.importc: "Math.sin", nodecl.}
proc sin*(x: float64): float64 {.importc: "Math.sin", nodecl.}
proc tan*(x: float32): float32 {.importc: "Math.tan", nodecl.}
proc tan*(x: float64): float64 {.importc: "Math.tan", nodecl.}
proc tanh*[T: float32|float64](x: T): T =
var y = exp(2.0*x)
return (y-1.0)/(y+1.0)
proc `mod`*(x, y: float32): float32 {.importcpp: "# % #".}
proc `mod`*(x, y: float64): float64 {.importcpp: "# % #".}
## Computes the modulo operation for float operators.
proc round*[T: float32|float64](x: T, places: int = 0): T =
## Round a floating point number.
@ -350,6 +424,21 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
var mult = pow(10.0, places.T)
result = round0(x*mult)/mult
proc floorDiv*[T: SomeInteger](x, y: T): T =
## Floor division is conceptually defined as ``floor(x / y)``.
## This is different from the ``div`` operator, which is defined
## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv``
## rounds down.
result = x div y
let r = x mod y
if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1
proc floorMod*[T: SomeNumber](x, y: T): T =
## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y).
## This proc behaves the same as the ``%`` operator in python.
result = x mod y
if (result > 0 and y < 0) or (result < 0 and y > 0): result += y
when not defined(JS):
proc c_frexp*(x: float32, exponent: var int32): float32 {.
importc: "frexp", header: "<math.h>".}
@ -364,6 +453,28 @@ when not defined(JS):
var exp: int32
result = c_frexp(x, exp)
exponent = exp
when windowsCC89:
# taken from Go-lang Math.Log2
const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009
template log2Impl[T](x: T): T =
var exp: int32
var frac = frexp(x, exp)
# Make sure exact powers of two give an exact answer.
# Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1.
if frac == 0.5: return T(exp - 1)
log10(frac)*(1/ln2) + T(exp)
proc log2*(x: float32): float32 = log2Impl(x)
proc log2*(x: float64): float64 = log2Impl(x)
## Log2 returns the binary logarithm of x.
## The special cases are the same as for Log.
else:
proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
## Computes the binary logarithm (base 2) of `x`
else:
proc frexp*[T: float32|float64](x: T, exponent: var int): T =
if x == 0.0:
@ -414,21 +525,12 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
## `NaN`.
ord(T(0) < x) - ord(x < T(0))
proc `mod`*[T: float32|float64](x, y: T): T =
## Computes the modulo operation for float operators. Equivalent
## to ``x - y * floor(x/y)``. Note that the remainder will always
## have the same sign as the divisor.
##
## .. code-block:: nim
## echo (4.0 mod -3.1) # -2.2
result = if y == 0.0: x else: x - y * (x/y).floor
{.pop.}
{.pop.}
proc `^`*[T](x: T, y: Natural): T =
## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use
## `pow <#pow,float,float>` for negative exponents.
## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
## `pow <#pow,float,float>`_ for negative exponents.
when compiles(y >= T(0)):
assert y >= T(0)
else:
@ -445,26 +547,53 @@ proc `^`*[T](x: T, y: Natural): T =
x *= x
proc gcd*[T](x, y: T): T =
## Computes the greatest common divisor of ``x`` and ``y``.
## Computes the greatest common (positive) divisor of ``x`` and ``y``.
## Note that for floats, the result cannot always be interpreted as
## "greatest decimal `z` such that ``z*N == x and z*M == y``
## where N and M are positive integers."
var (x,y) = (x,y)
var (x, y) = (x, y)
while y != 0:
x = x mod y
swap x, y
abs x
proc gcd*(x, y: SomeInteger): SomeInteger =
## Computes the greatest common (positive) divisor of ``x`` and ``y``.
## Using binary GCD (aka Stein's) algorithm.
when x is SomeSignedInt:
var x = abs(x)
else:
var x = x
when y is SomeSignedInt:
var y = abs(y)
else:
var y = y
if x == 0:
return y
if y == 0:
return x
let shift = countTrailingZeroBits(x or y)
y = y shr countTrailingZeroBits(y)
while x != 0:
x = x shr countTrailingZeroBits(x)
if y > x:
swap y, x
x -= y
y shl shift
proc lcm*[T](x, y: T): T =
## Computes the least common multiple of ``x`` and ``y``.
x div gcd(x, y) * y
when isMainModule and not defined(JS):
when isMainModule and not defined(JS) and not windowsCC89:
# Check for no side effect annotation
proc mySqrt(num: float): float {.noSideEffect.} =
return sqrt(num)
# check gamma function
assert(gamma(5.0) == 24.0) # 4!
assert($tgamma(5.0) == $24.0) # 4!
assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
assert(erf(6.0) > erf(5.0))
@ -474,6 +603,12 @@ when isMainModule:
# Function for approximate comparison of floats
proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
block: # prod
doAssert prod([1, 2, 3, 4]) == 24
doAssert prod([1.5, 3.4]) == 5.1
let x: seq[float] = @[]
doAssert prod(x) == 1.0
block: # round() tests
# Round to 0 decimal places
doAssert round(54.652) ==~ 55.0
@ -550,3 +685,44 @@ when isMainModule:
assert sgn(Inf) == 1
assert sgn(NaN) == 0
block: # fac() tests
try:
discard fac(-1)
except AssertionError:
discard
doAssert fac(0) == 1
doAssert fac(1) == 1
doAssert fac(2) == 2
doAssert fac(3) == 6
doAssert fac(4) == 24
block: # floorMod/floorDiv
doAssert floorDiv(8, 3) == 2
doAssert floorMod(8, 3) == 2
doAssert floorDiv(8, -3) == -3
doAssert floorMod(8, -3) == -1
doAssert floorDiv(-8, 3) == -3
doAssert floorMod(-8, 3) == 1
doAssert floorDiv(-8, -3) == 2
doAssert floorMod(-8, -3) == -2
doAssert floorMod(8.0, -3.0) ==~ -1.0
doAssert floorMod(-8.5, 3.0) ==~ 0.5
block: # log
doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0)
doAssert log2(8.0'f64) == 3.0'f64
doAssert log2(4.0'f64) == 2.0'f64
doAssert log2(2.0'f64) == 1.0'f64
doAssert log2(1.0'f64) == 0.0'f64
doAssert classify(log2(0.0'f64)) == fcNegInf
doAssert log2(8.0'f32) == 3.0'f32
doAssert log2(4.0'f32) == 2.0'f32
doAssert log2(2.0'f32) == 1.0'f32
doAssert log2(1.0'f32) == 0.0'f32
doAssert classify(log2(0.0'f32)) == fcNegInf

View file

@ -22,7 +22,11 @@ elif defined(posix):
else:
{.error: "the memfiles module is not supported on your operating system!".}
import os
import os, streams
proc newEIO(msg: string): ref IOError =
new(result)
result.msg = msg
type
MemFile* = object ## represents a memory mapped file
@ -38,19 +42,20 @@ type
else:
handle: cint
{.deprecated: [TMemFile: MemFile].}
proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
mappedSize = -1, offset = 0): pointer =
## returns a pointer to a mapped portion of MemFile `m`
##
## ``mappedSize`` of ``-1`` maps to the whole file, and
## ``offset`` must be multiples of the PAGE SIZE of your OS
if mode == fmAppend:
raise newEIO("The append mode is not supported.")
var readonly = mode == fmRead
when defined(windows):
result = mapViewOfFileEx(
m.mapHandle,
if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE,
int32(offset shr 32),
int32(offset and 0xffffffff),
if mappedSize == -1: 0 else: mappedSize,
@ -115,6 +120,9 @@ proc open*(filename: string, mode: FileMode = fmRead,
## mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512)
# The file can be resized only when write mode is used:
if mode == fmAppend:
raise newEIO("The append mode is not supported.")
assert newFileSize == -1 or mode != fmRead
var readonly = mode == fmRead
@ -123,6 +131,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
result.size = 0
when defined(windows):
let desiredAccess = GENERIC_READ
let shareMode = FILE_SHARE_READ
let flags = FILE_FLAG_RANDOM_ACCESS
template fail(errCode: OSErrorCode, msg: untyped) =
rollback()
if result.fHandle != 0: discard closeHandle(result.fHandle)
@ -135,11 +147,11 @@ proc open*(filename: string, mode: FileMode = fmRead,
winApiProc(
filename,
# GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE)
if readonly: GENERIC_READ else: GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ,
if readonly: desiredAccess else: desiredAccess or GENERIC_WRITE,
if readonly: shareMode else: shareMode or FILE_SHARE_WRITE,
nil,
if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING,
if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY,
if readonly: FILE_ATTRIBUTE_READONLY or flags else: FILE_ATTRIBUTE_NORMAL or flags,
0)
when useWinUnicode:
@ -174,7 +186,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
result.mem = mapViewOfFileEx(
result.mapHandle,
if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE,
int32(offset shr 32),
int32(offset and 0xffffffff),
if mappedSize == -1: 0 else: mappedSize,
@ -247,6 +259,28 @@ proc open*(filename: string, mode: FileMode = fmRead,
if close(result.handle) == 0:
result.handle = -1
proc flush*(f: var MemFile; attempts: Natural = 3) =
## Flushes `f`'s buffer for the number of attempts equal to `attempts`.
## If were errors an exception `OSError` will be raised.
var res = false
var lastErr: OSErrorCode
when defined(windows):
for i in 1..attempts:
res = flushViewOfFile(f.mem, 0) != 0
if res:
break
lastErr = osLastError()
if lastErr != ERROR_LOCK_VIOLATION.OSErrorCode:
raiseOSError(lastErr)
else:
for i in 1..attempts:
res = msync(f.mem, f.size, MS_SYNC or MS_INVALIDATE) == 0
if res:
break
lastErr = osLastError()
if lastErr != EBUSY.OSErrorCode:
raiseOSError(lastErr, "error flushing mapping")
proc close*(f: var MemFile) =
## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access.
@ -288,14 +322,12 @@ type MemSlice* = object ## represent slice of a MemFile for iteration over deli
proc `==`*(x, y: MemSlice): bool =
## Compare a pair of MemSlice for strict equality.
proc memcmp(a, b: pointer, n:int):int {.importc: "memcmp",header: "string.h".}
result = (x.size == y.size and memcmp(x.data, y.data, x.size) == 0)
result = (x.size == y.size and equalMem(x.data, y.data, x.size))
proc `$`*(ms: MemSlice): string {.inline.} =
## Return a Nim string built from a MemSlice.
var buf = newString(ms.size)
copyMem(addr(buf[0]), ms.data, ms.size)
buf[ms.size] = '\0'
result = buf
iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} =
@ -364,9 +396,9 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T
## echo line
for ms in memSlices(mfile, delim, eat):
buf.setLen(ms.size)
copyMem(addr(buf[0]), ms.data, ms.size)
buf[ms.size] = '\0'
setLen(buf.string, ms.size)
if ms.size > 0:
copyMem(addr buf[0], ms.data, ms.size)
yield buf
iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} =
@ -384,3 +416,68 @@ iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.}
var buf = TaintedString(newStringOfCap(80))
for line in lines(mfile, buf, delim, eat):
yield buf
type
MemMapFileStream* = ref MemMapFileStreamObj ## a stream that encapsulates a `MemFile`
MemMapFileStreamObj* = object of Stream
mf: MemFile
mode: FileMode
pos: ByteAddress
proc mmsClose(s: Stream) =
MemMapFileStream(s).pos = -1
close(MemMapFileStream(s).mf)
proc mmsFlush(s: Stream) = flush(MemMapFileStream(s).mf)
proc mmsAtEnd(s: Stream): bool = (MemMapFileStream(s).pos >= MemMapFileStream(s).mf.size) or
(MemMapFileStream(s).pos < 0)
proc mmsSetPosition(s: Stream, pos: int) =
if pos > MemMapFileStream(s).mf.size or pos < 0:
raise newEIO("cannot set pos in stream")
MemMapFileStream(s).pos = pos
proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos
proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem)
let p = cast[ByteAddress](MemMapFileStream(s).pos)
let l = min(bufLen, MemMapFileStream(s).mf.size - p)
moveMem(buffer, cast[pointer](startAddress + p), l)
result = l
proc mmsReadData(s: Stream, buffer: pointer, bufLen: int): int =
result = mmsPeekData(s, buffer, bufLen)
inc(MemMapFileStream(s).pos, result)
proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) =
if MemMapFileStream(s).mode == fmRead:
raise newEIO("cannot write to read-only stream")
let size = MemMapFileStream(s).mf.size
if MemMapFileStream(s).pos + bufLen > size:
raise newEIO("cannot write to stream")
let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) +
cast[ByteAddress](MemMapFileStream(s).pos)
moveMem(cast[pointer](p), buffer, bufLen)
inc(MemMapFileStream(s).pos, bufLen)
proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, fileSize: int = -1):
MemMapFileStream =
## creates a new stream from the file named `filename` with the mode `mode`.
## Raises ## `EOS` if the file cannot be opened. See the `system
## <system.html>`_ module for a list of available FileMode enums.
## ``fileSize`` can only be set if the file does not exist and is opened
## with write access (e.g., with fmReadWrite).
var mf: MemFile = open(filename, mode, newFileSize = fileSize)
new(result)
result.mode = mode
result.mf = mf
result.closeImpl = mmsClose
result.atEndImpl = mmsAtEnd
result.setPositionImpl = mmsSetPosition
result.getPositionImpl = mmsGetPosition
result.readDataImpl = mmsReadData
result.peekDataImpl = mmsPeekData
result.writeDataImpl = mmsWriteData
result.flushImpl = mmsFlush

View file

@ -12,8 +12,6 @@ type
mt: array[0..623, uint32]
index: int
{.deprecated: [TMersenneTwister: MersenneTwister].}
proc newMersenneTwister*(seed: uint32): MersenneTwister =
result.index = 0
result.mt[0] = seed

View file

@ -13,8 +13,6 @@ type
MimeDB* = object
mimes: StringTableRef
{.deprecated: [TMimeDB: MimeDB].}
const mimes* = {
"ez": "application/andrew-inset",
"anx": "application/annodex",
@ -233,6 +231,7 @@ const mimes* = {
"xcf": "application/x-xcf",
"fig": "application/x-xfig",
"xpi": "application/x-xpinstall",
"wasm": "application/wasm",
"amr": "audio/amr",
"awb": "audio/amr-wb",
"amr": "audio/amr",

View file

@ -31,7 +31,7 @@ else:
export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
Sockaddr_in6,
Sockaddr_in6, Sockaddr_storage,
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto,
freeAddrInfo
@ -85,9 +85,6 @@ type
length*: int
addrList*: seq[string]
{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
when useWinVersion:
let
osInvalidSocket* = winlean.INVALID_SOCKET
@ -251,9 +248,10 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
hints.ai_socktype = toInt(sockType)
hints.ai_protocol = toInt(protocol)
# OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
# FreeBSD doesn't support AI_V4MAPPED but defines the macro.
# FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android):
# https://dev.haiku-os.org/ticket/14323
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku):
if domain == AF_INET6:
hints.ai_flags = AI_V4MAPPED
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
@ -395,7 +393,15 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
result.addrtype = AF_INET6
else:
raiseOSError(osLastError(), "unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list)
if result.addrtype == AF_INET:
result.addrlist = @[]
var i = 0
while not isNil(s.h_addrlist[i]):
var inaddr_ptr = cast[ptr InAddr](s.h_addr_list[i])
result.addrlist.add($inet_ntoa(inaddr_ptr[]))
inc(i)
else:
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
@ -416,7 +422,15 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
result.addrtype = AF_INET6
else:
raiseOSError(osLastError(), "unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list)
if result.addrtype == AF_INET:
result.addrlist = @[]
var i = 0
while not isNil(s.h_addrlist[i]):
var inaddr_ptr = cast[ptr InAddr](s.h_addr_list[i])
result.addrlist.add($inet_ntoa(inaddr_ptr[]))
inc(i)
else:
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
proc getHostname*(): string {.tags: [ReadIOEffect].} =
@ -600,8 +614,12 @@ proc setBlocking*(s: SocketHandle, blocking: bool) =
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
when useWinVersion:
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
else:
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
FD_ZERO(fd)
@ -620,7 +638,7 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
inc(i)
setLen(s, L)
proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated.} =
proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated: "use selectRead instead".} =
## When a socket in ``readfds`` is ready to be read from then a non-zero
## value will be returned specifying the count of the sockets which can be
## read from. The sockets which can be read from will also be removed

View file

@ -41,7 +41,7 @@
## immediately.
##
## .. code-block:: Nim
## var socket = newSocket()
## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
## socket.sendTo("192.168.0.1", Port(27960), "status\n")
##
## Creating a server
@ -58,17 +58,13 @@
## You can then begin accepting connections using the ``accept`` procedure.
##
## .. code-block:: Nim
## var client = new Socket
## var client: Socket
## var address = ""
## while true:
## socket.acceptAddr(client, address)
## echo("Client connected from: ", address)
##
## **Note:** The ``client`` variable is initialised with ``new Socket`` **not**
## ``newSocket()``. The difference is that the latter creates a new file
## descriptor.
{.deadCodeElim: on.}
{.deadCodeElim: on.} # dce option deprecated
import nativesockets, os, strutils, parseutils, times, sets, options
export Port, `$`, `==`
export Domain, SockType, Protocol
@ -110,9 +106,6 @@ when defineSsl:
serverGetPskFunc: SslServerGetPskFunc
clientGetPskFunc: SslClientGetPskFunc
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
TSSLAcceptResult: SSLAcceptResult].}
else:
type
SslContext* = void # TODO: Workaround #4797.
@ -159,10 +152,6 @@ type
Peek,
SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
{.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError,
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
TSocketImpl: SocketImpl].}
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
@ -176,8 +165,6 @@ type
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
{.deprecated: [TIpAddress: IpAddress].}
proc socketError*(socket: Socket, err: int = -1, async = false,
lastError = (-1).OSErrorCode): void {.gcsafe.}
@ -240,7 +227,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
raiseOSError(osLastError())
result = newSocket(fd, domain, sockType, protocol, buffered)
proc parseIPv4Address(address_str: string): IpAddress =
proc parseIPv4Address(addressStr: string): IpAddress =
## Parses IPv4 adresses
## Raises EInvalidValue on errors
var
@ -250,15 +237,15 @@ proc parseIPv4Address(address_str: string): IpAddress =
result.family = IpAddressFamily.IPv4
for i in 0 .. high(address_str):
if address_str[i] in strutils.Digits: # Character is a number
for i in 0 .. high(addressStr):
if addressStr[i] in strutils.Digits: # Character is a number
currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0'))
cast[uint16](ord(addressStr[i]) - ord('0'))
if currentByte > 255'u16:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator
elif addressStr[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
@ -274,11 +261,11 @@ proc parseIPv4Address(address_str: string): IpAddress =
raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): IpAddress =
proc parseIPv6Address(addressStr: string): IpAddress =
## Parses IPv6 adresses
## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6
if address_str.len < 2:
if addressStr.len < 2:
raise newException(ValueError, "Invalid IP Address")
var
@ -291,7 +278,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
v4StartPos = -1
byteCount = 0
for i,c in address_str:
for i,c in addressStr:
if c == ':':
if not seperatorValid:
raise newException(ValueError,
@ -302,7 +289,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
"Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount
seperatorValid = false
elif i != 0 and i != high(address_str):
elif i != 0 and i != high(addressStr):
if groupCount >= 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
@ -312,11 +299,11 @@ proc parseIPv6Address(address_str: string): IpAddress =
groupCount.inc()
if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with ::
if address_str[1] != ':':
if addressStr[1] != ':':
raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':':
else: # i == high(addressStr) - only valid if address ends with ::
if addressStr[high(addressStr)-1] != ':':
raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"")
lastWasColon = true
@ -354,7 +341,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
groupCount.inc()
else: # Must parse IPv4 address
for i,c in address_str[v4StartPos..high(address_str)]:
for i,c in addressStr[v4StartPos..high(addressStr)]:
if c in strutils.Digits: # Character is a number
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32:
@ -395,25 +382,69 @@ proc parseIPv6Address(address_str: string): IpAddress =
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): IpAddress =
proc parseIpAddress*(addressStr: string): IpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:
raise newException(ValueError, "IP Address string is nil")
if address_str.contains(':'):
return parseIPv6Address(address_str)
if addressStr.len == 0:
raise newException(ValueError, "IP Address string is empty")
if addressStr.contains(':'):
return parseIPv6Address(addressStr)
else:
return parseIPv4Address(address_str)
return parseIPv4Address(addressStr)
proc isIpAddress*(address_str: string): bool {.tags: [].} =
proc isIpAddress*(addressStr: string): bool {.tags: [].} =
## Checks if a string is an IP address
## Returns true if it is, false otherwise
try:
discard parseIpAddress(address_str)
discard parseIpAddress(addressStr)
except ValueError:
return false
return true
proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage,
sl: var Socklen) =
## Converts `IpAddress` and `Port` to `SockAddr` and `Socklen`
let port = htons(uint16(port))
case address.family
of IpAddressFamily.IPv4:
sl = sizeof(Sockaddr_in).Socklen
let s = cast[ptr Sockaddr_in](addr sa)
s.sin_family = type(s.sin_family)(toInt(AF_INET))
s.sin_port = port
copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0],
sizeof(s.sin_addr))
of IpAddressFamily.IPv6:
sl = sizeof(Sockaddr_in6).Socklen
let s = cast[ptr Sockaddr_in6](addr sa)
s.sin6_family = type(s.sin6_family)(toInt(AF_INET6))
s.sin6_port = port
copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0],
sizeof(s.sin6_addr))
proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: Socklen,
address: var IpAddress, port: var Port) =
if sa.ss_family.int == toInt(AF_INET) and sl == sizeof(Sockaddr_in).Socklen:
address = IpAddress(family: IpAddressFamily.IPv4)
let s = cast[ptr Sockaddr_in](sa)
copyMem(addr address.address_v4[0], addr s.sin_addr,
sizeof(address.address_v4))
port = ntohs(s.sin_port).Port
elif sa.ss_family.int == toInt(AF_INET6) and
sl == sizeof(Sockaddr_in6).Socklen:
address = IpAddress(family: IpAddressFamily.IPv6)
let s = cast[ptr Sockaddr_in6](sa)
copyMem(addr address.address_v6[0], addr s.sin6_addr,
sizeof(address.address_v6))
port = ntohs(s.sin6_port).Port
else:
raise newException(ValueError, "Neither IPv4 nor IPv6")
proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6,
sl: Socklen, address: var IpAddress, port: var Port) {.inline.} =
## Converts `SockAddr` and `Socklen` to `IpAddress` and `Port`. Raises
## `ObjectConversionError` in case of invalid `sa` and `sl` arguments.
fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port)
when defineSsl:
CRYPTO_malloc_init()
doAssert SslLibraryInit() == 1
@ -552,7 +583,7 @@ when defineSsl:
proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar;
max_psk_len: cuint): cuint {.cdecl.} =
let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX)
let hintString = if hint == nil: nil else: $hint
let hintString = if hint == nil: "" else: $hint
let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
if psk.len.cuint > max_psk_len:
return 0
@ -622,7 +653,7 @@ when defineSsl:
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
handshake: SslHandshakeType,
hostname: string = nil) =
hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname
@ -636,7 +667,7 @@ when defineSsl:
wrapSocket(ctx, socket)
case handshake
of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname):
if hostname.len > 0 and not isIpAddress(hostname):
# Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
@ -754,16 +785,12 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
## The resulting client will inherit any properties of the server socket. For
## example: whether the socket is buffered or not.
##
## **Note**: ``client`` must be initialised (with ``new``), this function
## makes no effort to initialise the ``client`` variable.
##
## The ``accept`` call may result in an error if the connecting socket
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
## flag is specified then this error will not be raised and instead
## accept will be called again.
assert(client != nil)
assert client.fd.int <= 0, "Client socket needs to be initialised with " &
"`new`, not `newSocket`."
if client.isNil:
new(client)
let ret = accept(server.fd)
let sock = ret[0]
@ -775,6 +802,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
else:
address = ret[1]
client.fd = sock
client.domain = getSockDomain(sock)
client.isBuffered = server.isBuffered
# Handle SSL.
@ -843,9 +871,6 @@ proc accept*(server: Socket, client: var Socket,
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
## socket.
##
## **Note**: ``client`` must be initialised (with ``new``), this function
## makes no effort to initialise the ``client`` variable.
##
## The ``accept`` call may result in an error if the connecting socket
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
## flag is specified then this error will not be raised and instead
@ -932,7 +957,7 @@ when defined(posix) and not defined(nimdoc):
raise newException(ValueError, "socket path too long")
copyMem(addr result.sun_path, path.cstring, path.len + 1)
when defined(posix):
when defined(posix) or defined(nimdoc):
proc connectUnix*(socket: Socket, path: string) =
## Connects to Unix socket on `path`.
## This only works on Unix-style systems: Mac OS X, BSD and Linux
@ -1120,7 +1145,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
return 1
let sslPending = SSLPending(socket.sslHandle)
if sslPending != 0:
return sslPending
return min(sslPending, size)
var startTime = epochTime()
let selRet = select(socket, timeout - int(waited * 1000.0))
@ -1303,6 +1328,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
## used. Therefore if ``socket`` contains something in its buffer this
## function will make no effort to return it.
assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket")
# TODO: Buffered sockets
data.setLen(length)
var sockAddress: Sockaddr_in
@ -1372,23 +1398,25 @@ proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
result = send(socket, cstring(data), data.len) == data.len
proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
size: int, af: Domain = AF_INET, flags = 0'i32): int {.
size: int, af: Domain = AF_INET, flags = 0'i32) {.
tags: [WriteIOEffect].} =
## This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified
## this function will try each IP of that hostname.
##
## If an error occurs an OSError exception will be raised.
##
## **Note:** You may wish to use the high-level version of this function
## which is defined below.
##
## **Note:** This proc is not available for SSL sockets.
assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket")
assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
var aiList = getAddrInfo(address, port, af)
var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol)
# try all possibilities:
var success = false
var it = aiList
var result = 0
while it != nil:
result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr,
it.ai_addrlen.SockLen)
@ -1397,16 +1425,22 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
break
it = it.ai_next
let osError = osLastError()
freeAddrInfo(aiList)
if not success:
raiseOSError(osError)
proc sendTo*(socket: Socket, address: string, port: Port,
data: string): int {.tags: [WriteIOEffect].} =
data: string) {.tags: [WriteIOEffect].} =
## This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified
## this function will try each IP of that hostname.
##
## If an error occurs an OSError exception will be raised.
##
## This is the high-level version of the above ``sendTo`` function.
result = socket.sendTo(address, port, cstring(data), data.len)
socket.sendTo(address, port, cstring(data), data.len, socket.domain)
proc isSsl*(socket: Socket): bool =
@ -1664,6 +1698,9 @@ proc connect*(socket: Socket, address: string, port = Port(0),
if selectWrite(s, timeout) != 1:
raise newException(TimeoutError, "Call to 'connect' timed out.")
else:
let res = getSockOptInt(socket.fd, SOL_SOCKET, SO_ERROR)
if res != 0:
raiseOSError(OSErrorCode(res))
when defineSsl and not defined(nimdoc):
if socket.isSSL:
socket.fd.setBlocking(true)

View file

@ -30,7 +30,6 @@ when not declared(system.StackTrace):
type StackTrace = object
lines: array[0..20, cstring]
files: array[0..20, cstring]
{.deprecated: [TStackTrace: StackTrace].}
proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i]
# We use a simple hash table of bounded size to keep track of the stack traces:
@ -39,7 +38,6 @@ type
total: int
st: StackTrace
ProfileData = array[0..64*1024-1, ptr ProfileEntry]
{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].}
proc `==`(a, b: StackTrace): bool =
for i in 0 .. high(a.lines):

View file

@ -23,11 +23,9 @@ type
fuzz: int32 ##
count: int32 ##
{.deprecated: [Toid: Oid].}
proc `==`*(oid1: Oid, oid2: Oid): bool =
## Compare two Mongo Object IDs for equality
return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count)
## Compare two Mongo Object IDs for equality
return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count)
proc hexbyte*(hex: char): int =
case hex
@ -71,7 +69,7 @@ proc genOid*(): Oid =
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
var t = getTime().int32
var t = getTime().toUnix.int32
var i = int32(atomicInc(incr))

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