fixes merge conflicts
This commit is contained in:
commit
f7d1902043
887 changed files with 48670 additions and 42944 deletions
|
|
@ -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
47
lib/core/macrocache.nim
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>`_.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
29
lib/genode/env.nim
Normal 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
|
||||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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``
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {.
|
||||
|
|
|
|||
|
|
@ -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 = @[]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
96
lib/js/jscore.nim
Normal 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.}
|
||||
|
|
@ -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`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
36
lib/nintendoswitch/switch_memory.nim
Normal file
36
lib/nintendoswitch/switch_memory.nim
Normal 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.}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
[Package]
|
||||
name = "docutils"
|
||||
version = "0.9.0"
|
||||
author = "Andreas Rumpf"
|
||||
description = "Nimrod's reStructuredText processor."
|
||||
license = "MIT"
|
||||
5
lib/packages/docutils/docutils.nimble
Normal file
5
lib/packages/docutils/docutils.nimble
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
name = "docutils"
|
||||
version = "0.10.0"
|
||||
author = "Andreas Rumpf"
|
||||
description = "Nim's reStructuredText processor."
|
||||
license = "MIT"
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
{.deadCodeElim:on.}
|
||||
{.deadCodeElim: on.} # dce option deprecated
|
||||
|
||||
from posix import SocketHandle
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>".}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
506
lib/posix/posix_nintendoswitch.nim
Normal file
506
lib/posix/posix_nintendoswitch.nim
Normal 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
|
||||
586
lib/posix/posix_nintendoswitch_consts.nim
Normal file
586
lib/posix/posix_nintendoswitch_consts.nim
Normal 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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.} =
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
#
|
||||
# =======================================================================
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = @[]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) =
|
||||
|
|
|
|||
|
|
@ -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!")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -40,8 +40,6 @@ type
|
|||
offsetBase*: int # use ``offsetBase + bufpos`` to get the offset
|
||||
refillChars: set[char]
|
||||
|
||||
{.deprecated: [TBaseLexer: BaseLexer].}
|
||||
|
||||
const
|
||||
chrSize = sizeof(char)
|
||||
|
||||
|
|
|
|||
|
|
@ -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] =
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
149
lib/pure/net.nim
149
lib/pure/net.nim
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue