lib: Trim .nim files trailing whitespace

via OSX: find . -name '*.nim' -exec sed -i '' -E 's/[[:space:]]+$//' {} +
This commit is contained in:
Adam Strzelecki 2015-09-04 23:03:56 +02:00
commit 43bddf62dd
67 changed files with 2435 additions and 2435 deletions

View file

@ -41,7 +41,7 @@ type
Actor[In, Out] = object{.pure, final.}
i: Channel[Task[In, Out]]
t: TThread[ptr Actor[In, Out]]
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
{.deprecated: [TTask: Task, TActor: Actor].}
@ -83,7 +83,7 @@ proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
shallowCopy(t.data, msg)
send(receiver.i, t)
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
sender: ptr Channel[Out] = nil) =
## sends a message to `receiver`'s inbox.
var t: Task[In, Out]
@ -138,7 +138,7 @@ proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms and has a slight chance
## implemented as polling every `polling` ms and has a slight chance
## of failing since we check for every actor to be in `ready` state and not
## for messages still in ether. This will change in a later
## version, however.
@ -146,7 +146,7 @@ proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
while true:
var wait = false
for i in 0..high(a.actors):
if not a.actors[i].i.ready:
if not a.actors[i].i.ready:
wait = true
allReadyCount = 0
break
@ -222,7 +222,7 @@ proc spawn*[In](p: var ActorPool[In, void], input: In,
var t: Task[In, void]
setupTask()
schedule()
when not defined(testing) and isMainModule:
var
a: ActorPool[int, void]

View file

@ -195,10 +195,10 @@ proc read*(f: AsyncFile, size: int): Future[string] =
readBuffer.setLen(res)
f.offset.inc(res)
retFuture.complete(readBuffer)
if not cb(f.fd):
addRead(f.fd, cb)
return retFuture
proc readLine*(f: AsyncFile): Future[string] {.async.} =
@ -222,7 +222,7 @@ proc getFilePos*(f: AsyncFile): int64 =
proc setFilePos*(f: AsyncFile, pos: int64) =
## Sets the position of the file pointer that is used for read/write
## operations. The file's first byte has the index zero.
## operations. The file's first byte has the index zero.
f.offset = pos
when not defined(windows) and not defined(nimdoc):
let ret = lseek(f.fd.cint, pos, SEEK_SET)
@ -291,7 +291,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
retFuture.complete()
else:
var written = 0
proc cb(fd: AsyncFD): bool =
result = true
let remainderSize = data.len-written
@ -309,7 +309,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
result = false # We still have data to write.
else:
retFuture.complete()
if not cb(f.fd):
addWrite(f.fd, cb)
return retFuture

View file

@ -9,11 +9,11 @@
## This module implements a base64 encoder and decoder.
const
const
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
## encodes `s` into base64 representation. After `lineLen` characters, a
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
var total = ((len(s) + 2) div 3) * 4
var numLines = (total + lineLen - 1) div lineLen
@ -29,13 +29,13 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
var c = ord(s[i+2])
result[r] = cb64[a shr 2]
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result[r+3] = cb64[c and 0x3F]
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result[r+3] = cb64[c and 0x3F]
inc(r, 4)
inc(i, 3)
inc(currLine, 4)
if currLine >= lineLen and i != s.len-2:
for x in items(newLine):
if currLine >= lineLen and i != s.len-2:
for x in items(newLine):
result[r] = x
inc(r)
currLine = 0
@ -45,7 +45,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
var b = ord(s[i+1])
result[r] = cb64[a shr 2]
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result[r+2] = cb64[((b and 0x0F) shl 2)]
result[r+2] = cb64[((b and 0x0F) shl 2)]
result[r+3] = '='
if r+4 != result.len:
setLen(result, r+4)
@ -61,17 +61,17 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
#assert(r == result.len)
discard
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
encodeInternal(s, lineLen, newLine)
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
encodeInternal(s, lineLen, newLine)
proc decodeByte(b: char): int {.inline.} =
proc decodeByte(b: char): int {.inline.} =
case b
of '+': result = ord('>')
of '0'..'9': result = ord(b) + 4
@ -79,7 +79,7 @@ proc decodeByte(b: char): int {.inline.} =
of 'a'..'z': result = ord(b) - 71
else: result = 63
proc decode*(s: string): string =
proc decode*(s: string): string =
## decodes a string in base64 representation back into its original form.
## Whitespace is skipped.
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
@ -96,7 +96,7 @@ proc decode*(s: string): string =
var b = s[i+1].decodeByte
var c = s[i+2].decodeByte
var d = s[i+3].decodeByte
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
@ -105,19 +105,19 @@ proc decode*(s: string): string =
else: break
assert i == s.len
# adjust the length:
if i > 0 and s[i-1] == '=':
if i > 0 and s[i-1] == '=':
dec(r)
if i > 1 and s[i-2] == '=': dec(r)
setLen(result, r)
when isMainModule:
assert encode("leasure.") == "bGVhc3VyZS4="
assert encode("easure.") == "ZWFzdXJlLg=="
assert encode("asure.") == "YXN1cmUu"
assert encode("sure.") == "c3VyZS4="
const longText = """Man is distinguished, not only by his reason, but by this
singular passion from other animals, which is a lust of the mind,
singular passion from other animals, which is a lust of the mind,
that by a perseverance of delight in the continued and indefatigable
generation of knowledge, exceeds the short vehemence of any carnal
pleasure."""

View file

@ -13,26 +13,26 @@ import strutils
## Basic 2d support with vectors, points, matrices and some basic utilities.
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
## the translation part of matrix is ignored.
## the translation part of matrix is ignored.
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars.
##
## Quick start example:
##
##
## # Create a matrix which first rotates, then scales and at last translates
##
##
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
##
##
## # Create a 2d point at (100,0) and a vector (5,2)
##
## var pt:Point2d=point2d(100.0,0.0)
##
##
## var pt:Point2d=point2d(100.0,0.0)
##
## var vec:Vector2d=vector2d(5.0,2.0)
##
##
##
##
## pt &= m # transforms pt in place
##
##
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
##
##
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
@ -64,12 +64,12 @@ type
## not used for geometric transformations in 2d.
ax*,ay*,bx*,by*,tx*,ty*:float
Point2d* = object
## Implements a non-homogeneous 2d point stored as
## Implements a non-homogeneous 2d point stored as
## an `x` coordinate and an `y` coordinate.
x*,y*:float
Vector2d* = object
## Implements a 2d **direction vector** stored as
## an `x` coordinate and an `y` coordinate. Direction vector means,
Vector2d* = object
## Implements a 2d **direction vector** stored as
## an `x` coordinate and an `y` coordinate. Direction vector means,
## that when transforming a vector with a matrix, the translational
## part of the matrix is ignored.
x*,y*:float
@ -78,7 +78,7 @@ type
# Some forward declarations...
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
## Creates a new matrix.
## Creates a new matrix.
## `ax`,`ay` is the local x axis
## `bx`,`by` is the local y axis
## `tx`,`ty` is the translation
@ -99,7 +99,7 @@ let
YAXIS*:Vector2d=vector2d(0.0,1.0)
## Quick acces to an 2d y-axis unit vector
# ***************************************
# Private utils
# ***************************************
@ -114,13 +114,13 @@ proc safeArccos(v:float):float=
return arccos(clamp(v,-1.0,1.0))
template makeBinOpVector(s:expr)=
template makeBinOpVector(s:expr)=
## implements binary operators + , - , * and / for vectors
proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
template makeBinOpAssignVector(s:expr)=
template makeBinOpAssignVector(s:expr)=
## implements inplace binary operators += , -= , /= and *= for vectors
proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
@ -144,7 +144,7 @@ proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
## Concatenates matrices returning a new matrix.
# | a.AX a.AY 0 | | b.AX b.AY 0 |
# | a.BX a.BY 0 | * | b.BX b.BY 0 |
# | a.TX a.TY 1 | | b.TX b.TY 1 |
@ -153,7 +153,7 @@ proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
a.ax * b.ay + a.ay * b.by,
a.bx * b.ax + a.by * b.bx,
a.bx * b.ay + a.by * b.by,
a.tx * b.ax + a.ty * b.bx + b.tx,
a.tx * b.ax + a.ty * b.bx + b.tx,
a.tx * b.ay + a.ty * b.by + b.ty)
@ -169,13 +169,13 @@ proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
## Returns new a stretch matrix, which is a
## scale matrix with non uniform scale in x and y.
result.setElements(sx,0,0,sy,0,0)
proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
## Returns a new stretch matrix, which is a
## scale matrix with non uniform scale in x and y.
## `org` is used as stretch origin.
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
proc move*(dx,dy:float):Matrix2d {.noInit.} =
## Returns a new translation matrix.
result.setElements(1,0,0,1,dx,dy)
@ -187,7 +187,7 @@ proc move*(v:Vector2d):Matrix2d {.noInit.} =
proc rotate*(rad:float):Matrix2d {.noInit.} =
## Returns a new rotation matrix, which
## represents a rotation by `rad` radians
let
let
s=sin(rad)
c=cos(rad)
result.setElements(c,s,-s,c,0,0)
@ -200,7 +200,7 @@ proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
s=sin(rad)
c=cos(rad)
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
## Returns a new mirror matrix, mirroring
## around the line that passes through origo and
@ -211,7 +211,7 @@ proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
nd=1.0/(sqx+sqy) #used to normalize invector
xy2=v.x*v.y*2.0*nd
sqd=nd*(sqx-sqy)
if nd==Inf or nd==NegInf:
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
@ -230,7 +230,7 @@ proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
nd=1.0/(sqx+sqy) #used to normalize invector
xy2=v.x*v.y*2.0*nd
sqd=nd*(sqx-sqy)
if nd==Inf or nd==NegInf:
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
@ -238,47 +238,47 @@ proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
sqd,xy2,
xy2,-sqd,
org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd)
proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
## Returns a new skew matrix, which has its
## Returns a new skew matrix, which has its
## x axis rotated `xskew` radians from the local x axis, and
## y axis rotated `yskew` radians from the local y axis
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
proc `$`* (t:Matrix2d):string {.noInit.} =
## Returns a string representation of the matrix
return rtos(t.ax) & "," & rtos(t.ay) &
"," & rtos(t.bx) & "," & rtos(t.by) &
"," & rtos(t.bx) & "," & rtos(t.by) &
"," & rtos(t.tx) & "," & rtos(t.ty)
proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
## Checks if the transform is uniform, that is
## Checks if the transform is uniform, that is
## perpendicular axes of equal length, which means (for example)
## it cannot transform a circle into an ellipse.
## `tol` is used as tolerance for both equal length comparison
## `tol` is used as tolerance for both equal length comparison
## and perp. comparison.
#dot product=0 means perpendicular coord. system:
if abs(t.ax*t.bx+t.ay*t.by)<=tol:
if abs(t.ax*t.bx+t.ay*t.by)<=tol:
#subtract squared lengths of axes to check if uniform scaling:
if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol:
return true
return false
proc determinant*(t:Matrix2d):float=
## Computes the determinant of the matrix.
#NOTE: equivalent with perp.dot product for two 2d vectors
return t.ax*t.by-t.bx*t.ay
return t.ax*t.by-t.bx*t.ay
proc isMirroring* (m:Matrix2d):bool=
## Checks if the `m` is a mirroring matrix,
## which means it will reverse direction of a curve transformed with it
return m.determinant<0.0
proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
## Returns a new matrix, which is the inverse of the matrix
## If the matrix is not invertible (determinant=0), an EDivByZero
@ -286,7 +286,7 @@ proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
let d=m.determinant
if d==0.0:
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
result.setElements(
m.by/d,-m.ay/d,
-m.bx/d,m.ax/d,
@ -296,14 +296,14 @@ proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
## Checks if all elements of `m1`and `m2` is equal within
## a given tolerance `tol`.
return
return
abs(m1.ax-m2.ax)<=tol and
abs(m1.ay-m2.ay)<=tol and
abs(m1.bx-m2.bx)<=tol and
abs(m1.by-m2.by)<=tol and
abs(m1.tx-m2.tx)<=tol and
abs(m1.ty-m2.ty)<=tol
proc `=~`*(m1,m2:Matrix2d):bool=
## Checks if `m1`and `m2` is approximately equal, using a
## tolerance of 1e-6.
@ -350,16 +350,16 @@ proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
proc len*(v:Vector2d):float {.inline.}=
## Returns the length of the vector.
sqrt(v.x*v.x+v.y*v.y)
proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
## Sets the length of the vector, keeping its angle.
let fac=newlen/v.len
if newlen==0.0:
v.x=0.0
v.y=0.0
return
if fac==Inf or fac==NegInf:
#to short for float accuracy
#do as good as possible:
@ -368,30 +368,30 @@ proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
else:
v.x*=fac
v.y*=fac
proc sqrLen*(v:Vector2d):float {.inline.}=
## Computes the squared length of the vector, which is
## faster than computing the absolute length.
v.x*v.x+v.y*v.y
proc angle*(v:Vector2d):float=
## Returns the angle of the vector.
## Returns the angle of the vector.
## (The counter clockwise plane angle between posetive x axis and `v`)
result=arctan2(v.y,v.x)
if result<0.0: result+=DEG360
proc `$` *(v:Vector2d):string=
## String representation of `v`
result=rtos(v.x)
result.add(",")
result.add(rtos(v.y))
proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
## Concatenate vector `v` with a transformation matrix.
## Transforming a vector ignores the translational part
## of the matrix.
# | AX AY 0 |
# | X Y 1 | * | BX BY 0 |
# | 0 0 1 |
@ -403,7 +403,7 @@ proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
## Applies transformation `m` onto `v` in place.
## Transforming a vector ignores the translational part
## of the matrix.
# | AX AY 0 |
# | X Y 1 | * | BX BY 0 |
# | 0 0 1 |
@ -412,31 +412,31 @@ proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
v.x=newx
proc tryNormalize*(v:var Vector2d):bool=
proc tryNormalize*(v:var Vector2d):bool=
## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length (and thus no angle), it is left unmodified and
## If `v` has zero length (and thus no angle), it is left unmodified and
## false is returned, otherwise true is returned.
let mag=v.len
if mag==0.0:
return false
v.x/=mag
v.y/=mag
return true
proc normalize*(v:var Vector2d) {.inline.}=
proc normalize*(v:var Vector2d) {.inline.}=
## Modifies `v` to have a length of 1.0, keeping its angle.
## If `v` has zero length, an EDivByZero will be raised.
if not tryNormalize(v):
raise newException(DivByZeroError,"Cannot normalize zero length vector")
proc transformNorm*(v:var Vector2d,t:Matrix2d)=
## Applies a normal direction transformation `t` onto `v` in place.
## The resulting vector is *not* normalized. Transforming a vector ignores the
## translational part of the matrix. If the matrix is not invertible
## The resulting vector is *not* normalized. Transforming a vector ignores the
## translational part of the matrix. If the matrix is not invertible
## (determinant=0), an EDivByZero will be raised.
# transforming a normal is done by transforming
@ -469,16 +469,16 @@ proc transformInv*(v:var Vector2d,t:Matrix2d)=
proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
## Applies an inverse normal direction transformation `t` onto `v` in place.
## This is faster than creating an inverse
## matrix and transformNorm(...) it. Transforming a vector ignores the
## This is faster than creating an inverse
## matrix and transformNorm(...) it. Transforming a vector ignores the
## translational part of the matrix.
# normal inverse transform is done by transforming
# by the inverse of the transpose of the inverse of the org. matrix
# which is equivalent with transforming with the transpose.
# | | | AX AY 0 |^-1|^T|^-1 | AX BX 0 |
# | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 |
# | | | 0 0 1 | | | | 0 0 1 |
# | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 |
# | | | 0 0 1 | | | | 0 0 1 |
# This can be heavily reduced to:
let newx=t.ay*v.y+t.ax*v.x
v.y=t.by*v.y+t.bx*v.x
@ -489,19 +489,19 @@ proc rotate90*(v:var Vector2d) {.inline.}=
## without using any trigonometrics.
swap(v.x,v.y)
v.x= -v.x
proc rotate180*(v:var Vector2d){.inline.}=
## Quickly rotates vector `v` 180 degrees counter clockwise,
## without using any trigonometrics.
v.x= -v.x
v.y= -v.y
proc rotate270*(v:var Vector2d) {.inline.}=
## Quickly rotates vector `v` 270 degrees counter clockwise,
## without using any trigonometrics.
swap(v.x,v.y)
v.y= -v.y
proc rotate*(v:var Vector2d,rad:float) =
## Rotates vector `v` `rad` radians in place.
let
@ -510,18 +510,18 @@ proc rotate*(v:var Vector2d,rad:float) =
newx=c*v.x-s*v.y
v.y=c*v.y+s*v.x
v.x=newx
proc scale*(v:var Vector2d,fac:float){.inline.}=
## Scales vector `v` `rad` radians in place.
v.x*=fac
v.y*=fac
proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
## Stretches vector `v` `facx` times horizontally,
## and `facy` times vertically.
v.x*=facx
v.y*=facy
proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
## Mirrors vector `v` using `mirrvec` as mirror direction.
let
@ -530,20 +530,20 @@ proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
nd=1.0/(sqx+sqy) #used to normalize invector
xy2=mirrvec.x*mirrvec.y*2.0*nd
sqd=nd*(sqx-sqy)
if nd==Inf or nd==NegInf:
return #mirroring around a zero vector is arbitrary=>keep as is is fastest
let newx=xy2*v.y+sqd*v.x
v.y=v.x*xy2-sqd*v.y
v.x=newx
proc `-` *(v:Vector2d):Vector2d=
## Negates a vector
result.x= -v.x
result.y= -v.y
# declare templated binary operators
makeBinOpVector(`+`)
makeBinOpVector(`-`)
@ -556,27 +556,27 @@ makeBinOpAssignVector(`/=`)
proc dot*(v1,v2:Vector2d):float=
## Computes the dot product of two vectors.
## Computes the dot product of two vectors.
## Returns 0.0 if the vectors are perpendicular.
return v1.x*v2.x+v1.y*v2.y
proc cross*(v1,v2:Vector2d):float=
## Computes the cross product of two vectors, also called
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
## are parallel.
return v1.x*v2.y-v1.y*v2.x
proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
## Checks if two vectors approximately equals with a tolerance.
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
proc `=~` *(v1,v2:Vector2d):bool=
## Checks if two vectors approximately equals with a
## Checks if two vectors approximately equals with a
## hardcoded tolerance 1e-6
equals(v1,v2)
proc angleTo*(v1,v2:Vector2d):float=
## Returns the smallest of the two possible angles
## Returns the smallest of the two possible angles
## between `v1` and `v2` in radians.
var
nv1=v1
@ -584,7 +584,7 @@ proc angleTo*(v1,v2:Vector2d):float=
if not nv1.tryNormalize or not nv2.tryNormalize:
return 0.0 # zero length vector has zero angle to any other vector
return safeArccos(dot(nv1,nv2))
proc angleCCW*(v1,v2:Vector2d):float=
## Returns the counter clockwise plane angle from `v1` to `v2`,
## in range 0 - 2*PI
@ -592,7 +592,7 @@ proc angleCCW*(v1,v2:Vector2d):float=
if v1.cross(v2)>=0.0:
return a
return DEG360-a
proc angleCW*(v1,v2:Vector2d):float=
## Returns the clockwise plane angle from `v1` to `v2`,
## in range 0 - 2*PI
@ -612,32 +612,32 @@ proc turnAngle*(v1,v2:Vector2d):float=
proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
## Computes the bisector between v1 and v2 as a normalized vector.
## If one of the input vectors has zero length, a normalized version
## of the other is returned. If both input vectors has zero length,
## of the other is returned. If both input vectors has zero length,
## an arbitrary normalized vector is returned.
var
vmag1=v1.len
vmag2=v2.len
# zero length vector equals arbitrary vector, just change to magnitude to one to
# avoid zero division
if vmag1==0.0:
if vmag1==0.0:
if vmag2==0: #both are zero length return any normalized vector
return XAXIS
vmag1=1.0
if vmag2==0.0: vmag2=1.0
if vmag2==0.0: vmag2=1.0
let
x1=v1.x/vmag1
y1=v1.y/vmag1
x2=v2.x/vmag2
y2=v2.y/vmag2
result.x=(x1 + x2) * 0.5
result.y=(y1 + y2) * 0.5
if not result.tryNormalize():
# This can happen if vectors are colinear. In this special case
# there are actually two bisectors, we select just
# there are actually two bisectors, we select just
# one of them (x1,y1 rotated 90 degrees ccw).
result.x = -y1
result.y = x1
@ -651,13 +651,13 @@ proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
proc point2d*(x,y:float):Point2d =
result.x=x
result.y=y
proc sqrDist*(a,b:Point2d):float=
## Computes the squared distance between `a` and `b`
let dx=b.x-a.x
let dy=b.y-a.y
result=dx*dx+dy*dy
proc dist*(a,b:Point2d):float {.inline.}=
## Computes the absolute distance between `a` and `b`
result=sqrt(sqrDist(a,b))
@ -675,11 +675,11 @@ proc `$` *(p:Point2d):string=
result=rtos(p.x)
result.add(",")
result.add(rtos(p.y))
proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
## Concatenates a point `p` with a transform `t`,
## resulting in a new, transformed point.
# | AX AY 0 |
# | X Y 1 | * | BX BY 0 |
# | TX TY 1 |
@ -697,21 +697,21 @@ proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
## Applies the inverse of transformation `t` onto `p` in place.
## If the matrix is not invertable (determinant=0) , EDivByZero will
## be raised.
# | AX AY 0 | ^-1
# | X Y 1 | * | BX BY 0 |
# | TX TY 1 |
let d=t.determinant
if d==0.0:
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
let
let
newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d
p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d
p.x=newx
proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
## Adds a vector `v` to a point `p`, resulting
## Adds a vector `v` to a point `p`, resulting
## in a new point.
result.x=p.x+v.x
result.y=p.y+v.y
@ -722,7 +722,7 @@ proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
p.y+=v.y
proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
## Subtracts a vector `v` from a point `p`, resulting
## Subtracts a vector `v` from a point `p`, resulting
## in a new point.
result.x=p.x-v.x
result.y=p.y-v.y
@ -736,13 +736,13 @@ proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
## Subtracts a vector `v` from a point `p` in place.
p.x-=v.x
p.y-=v.y
proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
## Checks if two points approximately equals with a tolerance.
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
proc `=~`*(p1,p2:Point2d):bool {.inline.}=
## Checks if two vectors approximately equals with a
## Checks if two vectors approximately equals with a
## hardcoded tolerance 1e-6
equals(p1,p2)
@ -759,7 +759,7 @@ proc rotate*(p:var Point2d,rad:float)=
newx=p.x*c-p.y*s
p.y=p.y*c+p.x*s
p.x=newx
proc rotate*(p:var Point2d,rad:float,org:Point2d)=
## Rotates a point in place `rad` radians using `org` as
## center of rotation.
@ -769,25 +769,25 @@ proc rotate*(p:var Point2d,rad:float,org:Point2d)=
newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
p.x=newx
proc scale*(p:var Point2d,fac:float) {.inline.}=
## Scales a point in place `fac` times with world origo as origin.
p.x*=fac
p.y*=fac
proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
## Scales the point in place `fac` times with `org` as origin.
p.x=(p.x - org.x) * fac + org.x
p.y=(p.y - org.y) * fac + org.y
proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
## Scales a point in place non uniformly `facx` and `facy` times with
## Scales a point in place non uniformly `facx` and `facy` times with
## world origo as origin.
p.x*=facx
p.y*=facy
proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
## Scales the point in place non uniformly `facx` and `facy` times with
## Scales the point in place non uniformly `facx` and `facy` times with
## `org` as origin.
p.x=(p.x - org.x) * facx + org.x
p.y=(p.y - org.y) * facy + org.y
@ -814,21 +814,21 @@ proc area*(a,b,c:Point2d):float=
return abs(sgnArea(a,b,c))
proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
## Returns a point selected from `pts`, that has the closest
## Returns a point selected from `pts`, that has the closest
## euclidean distance to `p`
assert(pts.len>0) # must have at least one point
var
var
bestidx=0
bestdist=p.sqrDist(pts[0])
curdist:float
for idx in 1..high(pts):
curdist=p.sqrDist(pts[idx])
if curdist<bestdist:
bestidx=idx
bestdist=curdist
result=pts[bestidx]
@ -843,7 +843,7 @@ proc normAngle*(ang:float):float=
return ang
return ang mod DEG360
proc degToRad*(deg:float):float {.inline.}=
## converts `deg` degrees to radians
deg / RAD2DEGCONST
@ -852,4 +852,4 @@ proc radToDeg*(rad:float):float {.inline.}=
## converts `rad` radians to degrees
rad * RAD2DEGCONST

View file

@ -6,32 +6,32 @@ import unsigned, math, hashes
## Memory Utility Functions
proc newHeap*[T](): ptr T =
result = cast[ptr T](alloc0(sizeof(T)))
result = cast[ptr T](alloc0(sizeof(T)))
proc copyNew*[T](x: var T): ptr T =
var
size = sizeof(T)
mem = alloc(size)
copyMem(mem, x.addr, size)
var
size = sizeof(T)
mem = alloc(size)
copyMem(mem, x.addr, size)
return cast[ptr T](mem)
proc copyTo*[T](val: var T, dest: int) =
copyMem(pointer(dest), val.addr, sizeof(T))
proc copyTo*[T](val: var T, dest: int) =
copyMem(pointer(dest), val.addr, sizeof(T))
proc allocType*[T](): pointer = alloc(sizeof(T))
proc allocType*[T](): pointer = alloc(sizeof(T))
proc newShared*[T](): ptr T =
result = cast[ptr T](allocShared0(sizeof(T)))
result = cast[ptr T](allocShared0(sizeof(T)))
proc copyShared*[T](x: var T): ptr T =
var
size = sizeof(T)
mem = allocShared(size)
copyMem(mem, x.addr, size)
var
size = sizeof(T)
mem = allocShared(size)
copyMem(mem, x.addr, size)
return cast[ptr T](mem)
#------------------------------------------------------------------------------
## Pointer arithmetic
## Pointer arithmetic
proc `+`*(p: pointer, i: int): pointer {.inline.} =
cast[pointer](cast[int](p) + i)
@ -41,11 +41,11 @@ const
reProbeLimit = 12
minCopyWork = 4096
intSize = sizeof(int)
when sizeof(int) == 4: # 32bit
type
type
Raw = range[0..1073741823]
## The range of uint values that can be stored directly in a value slot
## when on a 32 bit platform
@ -56,40 +56,40 @@ elif sizeof(int) == 8: # 64bit
## The range of uint values that can be stored directly in a value slot
## when on a 64 bit platform
{.deprecated: [TRaw: Raw].}
else:
else:
{.error: "unsupported platform".}
type
type
Entry = tuple
key: int
value: int
EntryArr = ptr array[0..10_000_000, Entry]
PConcTable[K,V] = ptr object {.pure.}
len: int
used: int
active: int
copyIdx: int
copyDone: int
next: PConcTable[K,V]
next: PConcTable[K,V]
data: EntryArr
{.deprecated: [TEntry: Entry, TEntryArr: EntryArr.}
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
expVal: int, match: bool): int
expVal: int, match: bool): int
#------------------------------------------------------------------------------
# Create a new table
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
let
dataLen = max(nextPowerOfTwo(size), minTableSize)
dataSize = dataLen*sizeof(Entry)
dataMem = allocShared0(dataSize)
let
dataLen = max(nextPowerOfTwo(size), minTableSize)
dataSize = dataLen*sizeof(Entry)
dataMem = allocShared0(dataSize)
tableSize = 7 * intSize
tableMem = allocShared0(tableSize)
table = cast[PConcTable[K,V]](tableMem)
table = cast[PConcTable[K,V]](tableMem)
table.len = dataLen
table.used = 0
table.active = 0
@ -99,14 +99,14 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
table.data = cast[EntryArr](dataMem)
result = table
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Delete a table
proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
deallocShared(tbl.data)
deallocShared(tbl.data)
deallocShared(tbl)
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
table.data[i]
@ -119,15 +119,15 @@ proc pack[T](x: T): int {.inline.} =
result = (cast[int](x) shl 2)
#echo("packKey ",cast[int](x) , " -> ", result)
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
proc pop(x: int): int {.inline.} =
result = x and 0xFFFFFFFC'i32
# Pop the raw value off of our Key or Val
# Pop the raw value off of our Key or Val
proc popRaw(x: int): int {.inline.} =
result = x shr 2
result = x shr 2
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
proc popPtr[V](x: int): ptr V {.inline.} =
result = cast[ptr V](pop(x))
#echo("popPtr " & $x & " -> " & $cast[int](result))
@ -136,34 +136,34 @@ proc popPtr[V](x: int): ptr V {.inline.} =
# K or V is no longer valid use new table
const Ghost = 0xFFFFFFFC
proc isGhost(x: int): bool {.inline.} =
result = x == 0xFFFFFFFC
result = x == 0xFFFFFFFC
# Tombstone
# applied to V = K is dead
proc isTomb(x: int): bool {.inline.} =
# Tombstone
# applied to V = K is dead
proc isTomb(x: int): bool {.inline.} =
result = (x and 0x00000002) != 0
proc setTomb(x: int): int {.inline.} =
result = x or 0x00000002
# Prime
# K or V is in new table copied from old
proc isPrime(x: int): bool {.inline.} =
# K or V is in new table copied from old
proc isPrime(x: int): bool {.inline.} =
result = (x and 0x00000001) != 0
proc setPrime(x: int): int {.inline.} =
result = x or 0x00000001
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
##This is for i32 only need to override for i64
proc hashInt(x: int):int {.inline.} =
var h = uint32(x) #shr 2'u32
proc hashInt(x: int):int {.inline.} =
var h = uint32(x) #shr 2'u32
h = h xor (h shr 16'u32)
h *= 0x85ebca6b'u32
h = h xor (h shr 13'u32)
h *= 0xc2b2ae35'u32
h = h xor (h shr 16'u32)
h = h xor (h shr 16'u32)
result = int(h)
#------------------------------------------------------------------------------
@ -175,31 +175,31 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
#echo("A new table already exists, copy in progress")
return next
var
oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
newTable = newLFTable[K,V](oldLen*2)
success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable,
false, ATOMIC_RELAXED, ATOMIC_RELAXED)
if not success:
echo("someone beat us to it! delete table we just created and return his " & $cast[int](next))
deleteConcTable(newTable)
return next
deleteConcTable(newTable)
return next
else:
echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len)
return newTable
#------------------------------------------------------------------------------
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
result = false
when K is Raw:
if key1 == key2:
if key1 == key2:
result = true
else:
var
var
p1 = popPtr[K](key1)
p2 = popPtr[K](key2)
if p1 != nil and p2 != nil:
if p1 != nil and p2 != nil:
if cast[int](p1) == cast[int](p2):
return true
if p1[] == p2[]:
@ -214,53 +214,53 @@ proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable[K,V]): bool =
#echo("Copy idx " & $idx)
var
var
oldVal = 0
oldkey = 0
oldkey = 0
ok = false
result = false
#Block the key so no other threads waste time here
while not ok:
ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED)
#echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey))
#Prevent new values from appearing in the old table by priming
#echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey))
#Prevent new values from appearing in the old table by priming
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
while not isPrime(oldVal):
var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
else: oldVal.setPrime
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
else: oldVal.setPrime
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
if isPrime(box) and isTomb(box):
if isPrime(box) and isTomb(box):
return true
oldVal = box
break
#echo("oldVal was = ", oldVal, " set it to prime ", box)
if isPrime(oldVal) and isTomb(oldVal):
if isPrime(oldVal) and isTomb(oldVal):
#when not (K is Raw):
# deallocShared(popPtr[K](oldKey))
# deallocShared(popPtr[K](oldKey))
return false
if isTomb(oldVal):
if isTomb(oldVal):
echo("oldVal is Tomb!!!, should not happen")
if pop(oldVal) != 0:
if pop(oldVal) != 0:
result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
if result:
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
else:
#echo("copy slot failed")
if result:
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
else:
#echo("copy slot failed")
# Our copy is done so we disable the old slot
while not ok:
ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
oldVal.setTomb.setPrime , false, ATOMIC_RELAXED, ATOMIC_RELAXED)
#echo("disabled old slot")
#echo"---------------------"
#echo("disabled old slot")
#echo"---------------------"
#------------------------------------------------------------------------------
proc promote[K,V](table: var PConcTable[K,V]) =
var
newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED)
newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED)
deallocShared(table.data)
@ -270,52 +270,52 @@ proc promote[K,V](table: var PConcTable[K,V]) =
atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED)
atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED)
deallocShared(table.next)
atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
echo("new table swapped!")
#------------------------------------------------------------------------------
proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
var
proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
var
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
ok: bool
result = false
result = false
if workDone > 0:
#echo("len to copy =" & $oldLen)
#echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
#echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
while not ok:
ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
#if ok: echo("set copyDone")
# If the copy is done we can promote this table
#if ok: echo("set copyDone")
# If the copy is done we can promote this table
if copyDone + workDone >= oldLen:
# Swap new data
#echo("work is done!")
#echo("work is done!")
table.promote
result = true
#------------------------------------------------------------------------------
proc copySlotAndCheck[K,V](table: var PConcTable[K,V], idx: int):
PConcTable[K,V] =
var
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
result = newTable
if newTable != nil and copySlot(idx, table, newTable):
#echo("copied a single slot, idx = " & $idx)
result = newTable
if newTable != nil and copySlot(idx, table, newTable):
#echo("copied a single slot, idx = " & $idx)
if checkAndPromote(table, 1): return table
#------------------------------------------------------------------------------
proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
var
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
result = newTable
if newTable != nil:
var
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
result = newTable
if newTable != nil:
var
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
copyIdx = 0
work = min(oldLen, minCopyWork)
@ -324,54 +324,54 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
if copyDone < oldLen:
var ok: bool
while not ok:
ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
#echo("copy idx = ", copyIdx)
#echo("copy idx = ", copyIdx)
for i in 0..work-1:
var idx = (copyIdx + i) and (oldLen - 1)
var idx = (copyIdx + i) and (oldLen - 1)
if copySlot(idx, table, newTable):
workDone += 1
if workDone > 0:
#echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]()))
if checkAndPromote(table, workDone): return table
# In case a thread finished all the work then got stalled before promotion
# In case a thread finished all the work then got stalled before promotion
if checkAndPromote(table, 0): return table
#------------------------------------------------------------------------------
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
expVal: int, match: bool): int =
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
when K is Raw:
var idx = hashInt(key)
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
when K is Raw:
var idx = hashInt(key)
else:
var idx = popPtr[K](key)[].hash
var
nextTable: PConcTable[K,V]
var idx = popPtr[K](key)[].hash
var
nextTable: PConcTable[K,V]
probes = 1
# spin until we find a key slot or build and jump to next table
while true:
idx = idx and (table.len - 1)
# spin until we find a key slot or build and jump to next table
while true:
idx = idx and (table.len - 1)
#echo("try set idx = " & $idx & "for" & $key)
var
probedKey = 0
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
probedKey = 0
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
if openKey:
if val.isTomb:
#echo("val was tomb, bail, no reason to set an open slot to tomb")
return val
#increment used slots
#echo("found an open slot, total used = " &
#increment used slots
#echo("found an open slot, total used = " &
#$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED))
discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)
break # We found an open slot
#echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
break # We found an open slot
#echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
if keyEQ[K](probedKey, key):
#echo("we found the matching slot")
break # We found a matching slot
#echo("we found the matching slot")
break # We found a matching slot
if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb):
if key.isTomb: echo("Key is Tombstone")
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
@ -379,22 +379,22 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
#create next bigger table
nextTable = resize(table)
#help do some copying
#echo("help copy old table to new")
nextTable = helpCopy(table)
#echo("help copy old table to new")
nextTable = helpCopy(table)
#now setVal in the new table instead
#echo("jumping to next table to set val")
return setVal(nextTable, key, val, expVal, match)
#echo("jumping to next table to set val")
return setVal(nextTable, key, val, expVal, match)
else:
idx += 1
probes += 1
# Done spinning for a new slot
var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
if val == oldVal:
#echo("this val is alredy in the slot")
#echo("this val is alredy in the slot")
return oldVal
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
if nextTable == nil and
((oldVal == 0 and
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
if nextTable == nil and
((oldVal == 0 and
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
(isPrime(oldVal))):
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
@ -402,7 +402,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event")
nextTable = resize(table)
if nextTable != nil:
#echo("tomb old slot then set in new table")
#echo("tomb old slot then set in new table")
nextTable = copySlotAndCheck(table,idx)
return setVal(nextTable, key, val, expVal, match)
# Finally ready to add new val to table
@ -410,7 +410,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
if match and oldVal != expVal:
#echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal)
return oldVal
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
#echo("val set at table " & $cast[int](table))
if expVal != 0:
@ -427,48 +427,48 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
#------------------------------------------------------------------------------
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
#echo("-try get- key = " & $key)
when K is Raw:
when K is Raw:
var idx = hashInt(key)
else:
var idx = popPtr[K](key)[].hash
#echo("get idx ", idx)
var
var idx = popPtr[K](key)[].hash
#echo("get idx ", idx)
var
probes = 0
val: int
val: int
while true:
idx = idx and (table.len - 1)
var
idx = idx and (table.len - 1)
var
newTable: PConcTable[K,V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE)
probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
if keyEQ[K](probedKey, key):
#echo("found key after ", probes+1)
val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE)
if not isPrime(val):
if isTomb(val):
#echo("val was tomb but not prime")
#echo("val was tomb but not prime")
return 0
else:
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
return val
else:
newTable = copySlotAndCheck(table, idx)
return getVal(newTable, key)
return getVal(newTable, key)
else:
#echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
#echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
if probes >= reProbeLimit*4 or key.isTomb:
if newTable == nil:
#echo("too many probes and no new table ", key, " ", idx )
return 0
else:
else:
newTable = helpCopy(table)
return getVal(newTable, key)
idx += 1
probes += 1
#------------------------------------------------------------------------------
#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
# discard setVal(table, pack(key), pack(key), 0, false)
@ -476,33 +476,34 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
# discard setVal(table, pack(key), cast[int](val), 0, false)
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
when not (K is Raw):
when not (K is Raw):
var newKey = cast[int](copyShared(key))
else:
else:
var newKey = pack(key)
when not (V is Raw):
when not (V is Raw):
var newVal = cast[int](copyShared(val))
else:
else:
var newVal = pack(val)
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
when not (V is Raw):
if newVal != oldPtr and oldPtr != 0:
when not (V is Raw):
if newVal != oldPtr and oldPtr != 0:
deallocShared(cast[ptr V](oldPtr))
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
when not (V is Raw):
when not (K is Raw):
return popPtr[V](getVal(table, cast[int](key.addr)))[]
else:
else:
return popPtr[V](getVal(table, pack(key)))[]
else:
when not (K is Raw):
return popRaw(getVal(table, cast[int](key.addr)))
else:
return popRaw(getVal(table, pack(key)))
else:
return popRaw(getVal(table, pack(key)))
@ -512,7 +513,6 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
#proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} =
# getVal(table, key)
@ -528,16 +528,16 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
#Tests ----------------------------
when not defined(testing) and isMainModule:
import locks, times, mersenne
const
const
numTests = 100000
numThreads = 10
type
TestObj = tuple
thr: int
thr: int
f0: int
f1: int
@ -545,63 +545,63 @@ when not defined(testing) and isMainModule:
PDataArr = array[0..numTests-1, Data]
Dict = PConcTable[string,TestObj]
{.deprecated: [TTestObj: TestObj, TData: Data].}
var
var
thr: array[0..numThreads-1, Thread[Dict]]
table = newLFTable[string,TestObj](8)
table = newLFTable[string,TestObj](8)
rand = newMersenneTwister(2525)
proc createSampleData(len: int): PDataArr =
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
proc createSampleData(len: int): PDataArr =
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
for i in 0..len-1:
result[i].k = "mark" & $(i+1)
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
result[i].v.thr = 0
result[i].v.f0 = i+1
result[i].v.f1 = 0
result[i].v.f0 = i+1
result[i].v.f1 = 0
#echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr))
proc threadProc(tp: Dict) {.thread.} =
var t = cpuTime();
proc threadProc(tp: Dict) {.thread.} =
var t = cpuTime();
for i in 1..numTests:
var key = "mark" & $(i)
var got = table.get(key)
var got = table.get(key)
got.thr = cast[int](myThreadID[pointer]())
got.f1 = got.f1 + 1
got.f1 = got.f1 + 1
table.set(key, got)
t = cpuTime() - t
echo t
echo t
var testData = createSampleData(numTests)
for i in 0..numTests-1:
table.set(testData[i].k, testData[i].v)
var i = 0
while i < numThreads:
createThread(thr[i], threadProc, table)
i += 1
joinThreads(thr)
joinThreads(thr)
var fails = 0
for i in 0..numTests-1:
var got = table.get(testData[i].k)
for i in 0..numTests-1:
var got = table.get(testData[i].k)
if got.f0 != i+1 or got.f1 != numThreads:
fails += 1
echo(got)
echo("Failed read or write = ", fails)
#for i in 1..numTests:
# echo(i, " = ", hashInt(i) and 8191)

View file

@ -17,11 +17,11 @@ type
otherbits: char
case isLeaf: bool
of false: child: array[0..1, ref NodeObj[T]]
of true:
of true:
key: string
when T isnot void:
val: T
Node[T] = ref NodeObj[T]
CritBitTree*[T] = object ## The crit bit tree can either be used
## as a mapping from strings to
@ -66,7 +66,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
let ch = if it.byte < key.len: key[it.byte] else: '\0'
let dir = (1 + (ch.ord or it.otherBits.ord)) shr 8
it = it.child[dir]
var newOtherBits = 0
var newByte = 0
block blockX:
@ -84,7 +84,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
newOtherBits = newOtherBits xor 255
let ch = it.key[newByte]
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
var inner: Node[T]
new inner
new result
@ -93,7 +93,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
inner.otherBits = chr(newOtherBits)
inner.byte = newByte
inner.child[1 - dir] = result
var wherep = addr(c.root)
while true:
var p = wherep[]
@ -176,7 +176,7 @@ iterator leaves[T](n: Node[T]): Node[T] =
# XXX actually we could compute the necessary stack size in advance:
# it's roughly log2(c.count).
var stack = @[n]
while stack.len > 0:
while stack.len > 0:
var it = stack.pop
while not it.isLeaf:
stack.add(it.child[1])
@ -205,7 +205,7 @@ iterator items*[T](c: CritBitTree[T]): string =
iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
## yields all (key, value)-pairs of `c`.
for x in leaves(c.root): yield (x.key, x.val)
iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
for x in leaves(c.root): yield (x.key, x.val)
@ -251,7 +251,7 @@ iterator pairsWithPrefix*[T](c: CritBitTree[T],
## yields all (key, value)-pairs of `c` starting with `prefix`.
let top = allprefixedAux(c, prefix)
for x in leaves(top): yield (x.key, x.val)
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
prefix: string): tuple[key: string, val: var T] =
## yields all (key, value)-pairs of `c` starting with `prefix`.

View file

@ -27,18 +27,18 @@ type
SinglyLinkedList*[T] = object ## a singly linked list
head*, tail*: SinglyLinkedNode[T]
DoublyLinkedList*[T] = object ## a doubly linked list
head*, tail*: DoublyLinkedNode[T]
SinglyLinkedRing*[T] = object ## a singly linked ring
head*, tail*: SinglyLinkedNode[T]
DoublyLinkedRing*[T] = object ## a doubly linked ring
head*: DoublyLinkedNode[T]
{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
PDoublyLinkedNode: DoublyLinkedNode,
PDoublyLinkedNode: DoublyLinkedNode,
TSinglyLinkedNode: SinglyLinkedNodeObj,
PSinglyLinkedNode: SinglyLinkedNode,
TDoublyLinkedList: DoublyLinkedList,
@ -106,19 +106,19 @@ template findImpl() {.dirty.} =
for x in nodes(L):
if x.value == value: return x
iterator items*[T](L: DoublyLinkedList[T]): T =
iterator items*[T](L: DoublyLinkedList[T]): T =
## yields every value of `L`.
itemsListImpl()
iterator items*[T](L: SinglyLinkedList[T]): T =
iterator items*[T](L: SinglyLinkedList[T]): T =
## yields every value of `L`.
itemsListImpl()
iterator items*[T](L: SinglyLinkedRing[T]): T =
iterator items*[T](L: SinglyLinkedRing[T]): T =
## yields every value of `L`.
itemsRingImpl()
iterator items*[T](L: DoublyLinkedRing[T]): T =
iterator items*[T](L: DoublyLinkedRing[T]): T =
## yields every value of `L`.
itemsRingImpl()
@ -138,22 +138,22 @@ iterator mitems*[T](L: var DoublyLinkedRing[T]): var T =
## yields every value of `L` so that you can modify it.
itemsRingImpl()
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesListImpl()
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesListImpl()
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesRingImpl()
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesRingImpl()
@ -165,87 +165,87 @@ template dollarImpl() {.dirty.} =
result.add($x.value)
result.add("]")
proc `$`*[T](L: SinglyLinkedList[T]): string =
proc `$`*[T](L: SinglyLinkedList[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: DoublyLinkedList[T]): string =
proc `$`*[T](L: DoublyLinkedList[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: SinglyLinkedRing[T]): string =
proc `$`*[T](L: SinglyLinkedRing[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: DoublyLinkedRing[T]): string =
proc `$`*[T](L: DoublyLinkedRing[T]): string =
## turns a list into its string representation.
dollarImpl()
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc prepend*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} =
proc prepend*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} =
## prepends a node to `L`. Efficiency: O(1).
n.next = L.head
L.head = n
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
## prepends a node to `L`. Efficiency: O(1).
prepend(L, newSinglyLinkedNode(value))
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1).
n.next = nil
n.prev = L.tail
if L.tail != nil:
if L.tail != nil:
assert(L.tail.next == nil)
L.tail.next = n
L.tail = n
if L.head == nil: L.head = n
proc append*[T](L: var DoublyLinkedList[T], value: T) =
proc append*[T](L: var DoublyLinkedList[T], value: T) =
## appends a value to `L`. Efficiency: O(1).
append(L, newDoublyLinkedNode(value))
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
## prepends a node `n` to `L`. Efficiency: O(1).
n.prev = nil
n.next = L.head
@ -255,11 +255,11 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
L.head = n
if L.tail == nil: L.tail = n
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
## prepends a value to `L`. Efficiency: O(1).
prepend(L, newDoublyLinkedNode(value))
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
## removes `n` from `L`. Efficiency: O(1).
if n == L.tail: L.tail = n.prev
if n == L.head: L.head = n.next
@ -267,7 +267,7 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
if n.prev != nil: n.prev.next = n.next
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1).
if L.head != nil:
n.next = L.head
@ -279,11 +279,11 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
L.head = n
L.tail = n
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
## appends a value to `L`. Efficiency: O(1).
append(L, newSinglyLinkedNode(value))
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
## prepends a node `n` to `L`. Efficiency: O(1).
if L.head != nil:
n.next = L.head
@ -294,11 +294,11 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
L.tail = n
L.head = n
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
## prepends a value to `L`. Efficiency: O(1).
prepend(L, newSinglyLinkedNode(value))
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1).
if L.head != nil:
n.next = L.head
@ -310,13 +310,13 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
n.next = n
L.head = n
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
## appends a value to `L`. Efficiency: O(1).
append(L, newDoublyLinkedNode(value))
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
## prepends a node `n` to `L`. Efficiency: O(1).
if L.head != nil:
if L.head != nil:
n.next = L.head
n.prev = L.head.prev
L.head.prev.next = n
@ -326,17 +326,17 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
n.next = n
L.head = n
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
## prepends a value to `L`. Efficiency: O(1).
prepend(L, newDoublyLinkedNode(value))
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
## removes `n` from `L`. Efficiency: O(1).
n.next.prev = n.prev
n.prev.next = n.next
if n == L.head:
if n == L.head:
var p = L.head.prev
if p == L.head:
if p == L.head:
# only one element left:
L.head = nil
else:

View file

@ -77,7 +77,7 @@ proc dequeue*[T](q: var Queue[T]): T =
result = q.data[q.rd]
q.rd = (q.rd + 1) and q.mask
proc `$`*[T](q: Queue[T]): string =
proc `$`*[T](q: Queue[T]): string =
## turns a queue into its string representation.
result = "["
for x in items(q):
@ -95,7 +95,7 @@ when isMainModule:
q.add(6)
var second = q.dequeue
q.add(789)
assert first == 123
assert second == 9
assert($q == "[4, 56, 6, 789]")

View file

@ -6,7 +6,7 @@
# distribution, for details about the copyright.
#
## This module implements color handling for Nimrod. It is used by
## This module implements color handling for Nimrod. It is used by
## the ``graphics`` module.
import strutils
@ -18,15 +18,15 @@ type
proc `==` *(a, b: Color): bool {.borrow.}
## compares two colors.
template extract(a: Color, r, g, b: expr) {.immediate.}=
var r = a.int shr 16 and 0xff
var g = a.int shr 8 and 0xff
var b = a.int and 0xff
template rawRGB(r, g, b: int): expr =
Color(r shl 16 or g shl 8 or b)
template colorOp(op: expr) {.immediate.} =
extract(a, ar, ag, ab)
extract(b, br, bg, bb)
@ -39,24 +39,24 @@ proc satPlus(a, b: int): int {.inline.} =
proc satMinus(a, b: int): int {.inline.} =
result = a -% b
if result < 0: result = 0
proc `+`*(a, b: Color): Color =
## adds two colors: This uses saturated artithmetic, so that each color
## component cannot overflow (255 is used as a maximum).
colorOp(satPlus)
proc `-`*(a, b: Color): Color =
## subtracts two colors: This uses saturated artithmetic, so that each color
## component cannot overflow (255 is used as a maximum).
colorOp(satMinus)
proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
## extracts the red/green/blue components of the color `a`.
result.r = a.int shr 16 and 0xff
result.g = a.int shr 8 and 0xff
result.b = a.int and 0xff
proc intensity*(a: Color, f: float): Color =
proc intensity*(a: Color, f: float): Color =
## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
## dark) to 1.0 (full color intensity).
var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
@ -66,7 +66,7 @@ proc intensity*(a: Color, f: float): Color =
if g >% 255: g = 255
if b >% 255: b = 255
result = rawRGB(r, g, b)
template mix*(a, b: Color, fn: expr): expr =
## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
## R, G, and B. This is a template because `fn` should be inlined and the
@ -79,7 +79,7 @@ template mix*(a, b: Color, fn: expr): expr =
if y >% 255:
y = if y < 0: 0 else: 255
y
(bind extract)(a, ar, ag, ab)
(bind extract)(b, br, bg, bb)
(bind rawRGB)(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
@ -226,7 +226,7 @@ const
colWhiteSmoke* = Color(0xF5F5F5)
colYellow* = Color(0xFFFF00)
colYellowGreen* = Color(0x9ACD32)
colorNames = [
("aliceblue", colAliceBlue),
("antiquewhite", colAntiqueWhite),
@ -369,24 +369,24 @@ const
("yellow", colYellow),
("yellowgreen", colYellowGreen)]
proc `$`*(c: Color): string =
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 =
proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
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 parseColor*(name: string): Color =
## parses `name` to a color value. If no valid color could be
proc parseColor*(name: string): Color =
## parses `name` to a color value. If no valid color could be
## parsed ``EInvalidValue`` is raised.
if name[0] == '#':
result = Color(parseHexInt(name))
@ -396,10 +396,10 @@ proc parseColor*(name: string): Color =
result = colorNames[idx][1]
proc isColor*(name: string): bool =
## returns true if `name` is a known color name or a hexadecimal color
## returns true if `name` is a known color name or a hexadecimal color
## prefixed with ``#``.
if name[0] == '#':
for i in 1 .. name.len-1:
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:

View file

@ -17,7 +17,7 @@
import
math
const
EPS = 1.0e-7 ## Epsilon used for float comparisons.
@ -248,7 +248,7 @@ proc pow*(x, y: Complex): Complex =
var r = y.re*theta + y.im*ln(rho)
result.re = s*cos(r)
result.im = s*sin(r)
proc sin*(z: Complex): Complex =
## Returns the sine of `z`.
@ -387,7 +387,7 @@ when isMainModule:
var one = (1.0,0.0)
var tt = (10.0, 20.0)
var ipi = (0.0, -PI)
assert( a == a )
assert( (a-a) == z )
assert( (a+b) == z )
@ -403,7 +403,7 @@ when isMainModule:
assert( conjugate(a) == (1.0, -2.0) )
assert( sqrt(m1) == i )
assert( exp(ipi) =~ m1 )
assert( pow(a,b) =~ (-3.72999124927876, -1.68815826725068) )
assert( pow(z,a) =~ (0.0, 0.0) )
assert( pow(z,z) =~ (1.0, 0.0) )

View file

@ -18,7 +18,7 @@ when not defined(windows):
when defined(linux):
import linux
when defined(freebsd) or defined(macosx):
{.emit:"#include <sys/types.h>".}

View file

@ -11,7 +11,7 @@
import strtabs, times
proc parseCookies*(s: string): StringTableRef =
proc parseCookies*(s: string): StringTableRef =
## parses cookies into a string table.
result = newStringTable(modeCaseInsensitive)
var i = 0
@ -31,7 +31,7 @@ proc parseCookies*(s: string): StringTableRef =
proc setCookie*(key, value: string, domain = "", path = "",
expires = "", noName = false,
secure = false, httpOnly = false): string =
## Creates a command in the format of
## Creates a command in the format of
## ``Set-Cookie: key=value; Domain=...; ...``
result = ""
if not noName: result.add("Set-Cookie: ")
@ -45,10 +45,10 @@ proc setCookie*(key, value: string, domain = "", path = "",
proc setCookie*(key, value: string, expires: TimeInfo,
domain = "", path = "", noName = false,
secure = false, httpOnly = false): string =
## Creates a command in the format of
## Creates a command in the format of
## ``Set-Cookie: key=value; Domain=...; ...``
##
## **Note:** UTC is assumed as the timezone for ``expires``.
## **Note:** UTC is assumed as the timezone for ``expires``.
return setCookie(key, value, domain, path,
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"),
noname, secure, httpOnly)
@ -61,7 +61,7 @@ when isMainModule:
echo cookie
let start = "Set-Cookie: test=value; Expires="
assert cookie[0..start.high] == start
let table = parseCookies("uid=1; kp=2")
assert table["uid"] == "1"
assert table["kp"] == "2"

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## Converts between different character encodings. On UNIX, this uses
## Converts between different character encodings. On UNIX, this uses
## the `iconv`:idx: library, on Windows the Windows API.
import os, parseutils, strutils
@ -41,173 +41,173 @@ when defined(windows):
inc j
result = i == a.len and j == b.len
const
const
winEncodings = [
(1, "OEMCP"), # current OEM codepage
(037, "IBM037"), # IBM EBCDIC US-Canada
(437, "IBM437"), # OEM United States
(500, "IBM500"), # IBM EBCDIC International
(708, "ASMO-708"), # Arabic (ASMO 708)
(709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4)
(710, ""), # Arabic - Transparent Arabic
(720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS)
(737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS)
(775, "ibm775"), # OEM Baltic; Baltic (DOS)
(850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS)
(852, "ibm852"), # OEM Latin 2; Central European (DOS)
(855, "IBM855"), # OEM Cyrillic (primarily Russian)
(857, "ibm857"), # OEM Turkish; Turkish (DOS)
(858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol
(860, "IBM860"), # OEM Portuguese; Portuguese (DOS)
(861, "ibm861"), # OEM Icelandic; Icelandic (DOS)
(862, "DOS-862"), # OEM Hebrew; Hebrew (DOS)
(863, "IBM863"), # OEM French Canadian; French Canadian (DOS)
(864, "IBM864"), # OEM Arabic; Arabic (864)
(865, "IBM865"), # OEM Nordic; Nordic (DOS)
(866, "cp866"), # OEM Russian; Cyrillic (DOS)
(869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS)
(870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
(874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
(875, "cp875"), # IBM EBCDIC Greek Modern
(932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS)
(936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
(949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code)
(950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
(1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5)
(1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System
(1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
(1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
(1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
(1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
(1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
(1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
(1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
(1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
(1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
(1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
(1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
(1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications
(1250, "windows-1250"), # ANSI Central European; Central European (Windows)
(1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows)
(1252, "windows-1252"), # ANSI Latin 1; Western European (Windows)
(1253, "windows-1253"), # ANSI Greek; Greek (Windows)
(1254, "windows-1254"), # ANSI Turkish; Turkish (Windows)
(1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows)
(1256, "windows-1256"), # ANSI Arabic; Arabic (Windows)
(1257, "windows-1257"), # ANSI Baltic; Baltic (Windows)
(1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
(037, "IBM037"), # IBM EBCDIC US-Canada
(437, "IBM437"), # OEM United States
(500, "IBM500"), # IBM EBCDIC International
(708, "ASMO-708"), # Arabic (ASMO 708)
(709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4)
(710, ""), # Arabic - Transparent Arabic
(720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS)
(737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS)
(775, "ibm775"), # OEM Baltic; Baltic (DOS)
(850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS)
(852, "ibm852"), # OEM Latin 2; Central European (DOS)
(855, "IBM855"), # OEM Cyrillic (primarily Russian)
(857, "ibm857"), # OEM Turkish; Turkish (DOS)
(858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol
(860, "IBM860"), # OEM Portuguese; Portuguese (DOS)
(861, "ibm861"), # OEM Icelandic; Icelandic (DOS)
(862, "DOS-862"), # OEM Hebrew; Hebrew (DOS)
(863, "IBM863"), # OEM French Canadian; French Canadian (DOS)
(864, "IBM864"), # OEM Arabic; Arabic (864)
(865, "IBM865"), # OEM Nordic; Nordic (DOS)
(866, "cp866"), # OEM Russian; Cyrillic (DOS)
(869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS)
(870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
(874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
(875, "cp875"), # IBM EBCDIC Greek Modern
(932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS)
(936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
(949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code)
(950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
(1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5)
(1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System
(1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
(1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
(1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
(1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
(1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
(1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
(1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
(1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
(1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
(1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
(1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
(1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications
(1250, "windows-1250"), # ANSI Central European; Central European (Windows)
(1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows)
(1252, "windows-1252"), # ANSI Latin 1; Western European (Windows)
(1253, "windows-1253"), # ANSI Greek; Greek (Windows)
(1254, "windows-1254"), # ANSI Turkish; Turkish (Windows)
(1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows)
(1256, "windows-1256"), # ANSI Arabic; Arabic (Windows)
(1257, "windows-1257"), # ANSI Baltic; Baltic (Windows)
(1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
(1250, "cp-1250"), # ANSI Central European; Central European (Windows)
(1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows)
(1252, "cp-1252"), # ANSI Latin 1; Western European (Windows)
(1253, "cp-1253"), # ANSI Greek; Greek (Windows)
(1254, "cp-1254"), # ANSI Turkish; Turkish (Windows)
(1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows)
(1256, "cp-1256"), # ANSI Arabic; Arabic (Windows)
(1257, "cp-1257"), # ANSI Baltic; Baltic (Windows)
(1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
(1250, "cp-1250"), # ANSI Central European; Central European (Windows)
(1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows)
(1252, "cp-1252"), # ANSI Latin 1; Western European (Windows)
(1253, "cp-1253"), # ANSI Greek; Greek (Windows)
(1254, "cp-1254"), # ANSI Turkish; Turkish (Windows)
(1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows)
(1256, "cp-1256"), # ANSI Arabic; Arabic (Windows)
(1257, "cp-1257"), # ANSI Baltic; Baltic (Windows)
(1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
(1361, "Johab"), # Korean (Johab)
(10000, "macintosh"), # MAC Roman; Western European (Mac)
(10001, "x-mac-japanese"), # Japanese (Mac)
(10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
(10003, "x-mac-korean"), # Korean (Mac)
(10004, "x-mac-arabic"), # Arabic (Mac)
(10005, "x-mac-hebrew"), # Hebrew (Mac)
(10006, "x-mac-greek"), # Greek (Mac)
(10007, "x-mac-cyrillic"), # Cyrillic (Mac)
(10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
(10010, "x-mac-romanian"), # Romanian (Mac)
(10017, "x-mac-ukrainian"), # Ukrainian (Mac)
(10021, "x-mac-thai"), # Thai (Mac)
(10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac)
(10079, "x-mac-icelandic"), # Icelandic (Mac)
(10081, "x-mac-turkish"), # Turkish (Mac)
(10082, "x-mac-croatian"), # Croatian (Mac)
(12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications
(12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications
(20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS)
(20001, "x-cp20001"), # TCA Taiwan
(20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten)
(20003, "x-cp20003"), # IBM5550 Taiwan
(20004, "x-cp20004"), # TeleText Taiwan
(20005, "x-cp20005"), # Wang Taiwan
(20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
(20106, "x-IA5-German"), # IA5 German (7-bit)
(20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit)
(20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit)
(20127, "us-ascii"), # US-ASCII (7-bit)
(20261, "x-cp20261"), # T.61
(20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent
(20273, "IBM273"), # IBM EBCDIC Germany
(20277, "IBM277"), # IBM EBCDIC Denmark-Norway
(20278, "IBM278"), # IBM EBCDIC Finland-Sweden
(20280, "IBM280"), # IBM EBCDIC Italy
(20284, "IBM284"), # IBM EBCDIC Latin America-Spain
(20285, "IBM285"), # IBM EBCDIC United Kingdom
(20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended
(20297, "IBM297"), # IBM EBCDIC France
(20420, "IBM420"), # IBM EBCDIC Arabic
(20423, "IBM423"), # IBM EBCDIC Greek
(20424, "IBM424"), # IBM EBCDIC Hebrew
(20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended
(20838, "IBM-Thai"), # IBM EBCDIC Thai
(20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R)
(20871, "IBM871"), # IBM EBCDIC Icelandic
(20880, "IBM880"), # IBM EBCDIC Cyrillic Russian
(20905, "IBM905"), # IBM EBCDIC Turkish
(20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
(20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990)
(20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
(20949, "x-cp20949"), # Korean Wansung
(21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian
(21027, ""), # (deprecated)
(21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U)
(28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO)
(28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO)
(28593, "iso-8859-3"), # ISO 8859-3 Latin 3
(28594, "iso-8859-4"), # ISO 8859-4 Baltic
(28595, "iso-8859-5"), # ISO 8859-5 Cyrillic
(28596, "iso-8859-6"), # ISO 8859-6 Arabic
(28597, "iso-8859-7"), # ISO 8859-7 Greek
(28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
(28599, "iso-8859-9"), # ISO 8859-9 Turkish
(28603, "iso-8859-13"), # ISO 8859-13 Estonian
(28605, "iso-8859-15"), # ISO 8859-15 Latin 9
(29001, "x-Europa"), # Europa 3
(38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
(50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
(50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
(50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
(50225, "iso-2022-kr"), # ISO 2022 Korean
(50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
(50229, ""), # ISO 2022 Traditional Chinese
(50930, ""), # EBCDIC Japanese (Katakana) Extended
(50931, ""), # EBCDIC US-Canada and Japanese
(50933, ""), # EBCDIC Korean Extended and Korean
(50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese
(50936, ""), # EBCDIC Simplified Chinese
(50937, ""), # EBCDIC US-Canada and Traditional Chinese
(50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese
(51932, "euc-jp"), # EUC Japanese
(51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC)
(51949, "euc-kr"), # EUC Korean
(51950, ""), # EUC Traditional Chinese
(52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
(54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
(57002, "x-iscii-de"), # ISCII Devanagari
(57003, "x-iscii-be"), # ISCII Bengali
(57004, "x-iscii-ta"), # ISCII Tamil
(57005, "x-iscii-te"), # ISCII Telugu
(57006, "x-iscii-as"), # ISCII Assamese
(57007, "x-iscii-or"), # ISCII Oriya
(57008, "x-iscii-ka"), # ISCII Kannada
(57009, "x-iscii-ma"), # ISCII Malayalam
(57010, "x-iscii-gu"), # ISCII Gujarati
(57011, "x-iscii-pa"), # ISCII Punjabi
(65000, "utf-7"), # Unicode (UTF-7)
(65001, "utf-8")] # Unicode (UTF-8)
(1361, "Johab"), # Korean (Johab)
(10000, "macintosh"), # MAC Roman; Western European (Mac)
(10001, "x-mac-japanese"), # Japanese (Mac)
(10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
(10003, "x-mac-korean"), # Korean (Mac)
(10004, "x-mac-arabic"), # Arabic (Mac)
(10005, "x-mac-hebrew"), # Hebrew (Mac)
(10006, "x-mac-greek"), # Greek (Mac)
(10007, "x-mac-cyrillic"), # Cyrillic (Mac)
(10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
(10010, "x-mac-romanian"), # Romanian (Mac)
(10017, "x-mac-ukrainian"), # Ukrainian (Mac)
(10021, "x-mac-thai"), # Thai (Mac)
(10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac)
(10079, "x-mac-icelandic"), # Icelandic (Mac)
(10081, "x-mac-turkish"), # Turkish (Mac)
(10082, "x-mac-croatian"), # Croatian (Mac)
(12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications
(12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications
(20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS)
(20001, "x-cp20001"), # TCA Taiwan
(20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten)
(20003, "x-cp20003"), # IBM5550 Taiwan
(20004, "x-cp20004"), # TeleText Taiwan
(20005, "x-cp20005"), # Wang Taiwan
(20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
(20106, "x-IA5-German"), # IA5 German (7-bit)
(20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit)
(20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit)
(20127, "us-ascii"), # US-ASCII (7-bit)
(20261, "x-cp20261"), # T.61
(20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent
(20273, "IBM273"), # IBM EBCDIC Germany
(20277, "IBM277"), # IBM EBCDIC Denmark-Norway
(20278, "IBM278"), # IBM EBCDIC Finland-Sweden
(20280, "IBM280"), # IBM EBCDIC Italy
(20284, "IBM284"), # IBM EBCDIC Latin America-Spain
(20285, "IBM285"), # IBM EBCDIC United Kingdom
(20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended
(20297, "IBM297"), # IBM EBCDIC France
(20420, "IBM420"), # IBM EBCDIC Arabic
(20423, "IBM423"), # IBM EBCDIC Greek
(20424, "IBM424"), # IBM EBCDIC Hebrew
(20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended
(20838, "IBM-Thai"), # IBM EBCDIC Thai
(20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R)
(20871, "IBM871"), # IBM EBCDIC Icelandic
(20880, "IBM880"), # IBM EBCDIC Cyrillic Russian
(20905, "IBM905"), # IBM EBCDIC Turkish
(20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
(20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990)
(20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
(20949, "x-cp20949"), # Korean Wansung
(21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian
(21027, ""), # (deprecated)
(21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U)
(28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO)
(28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO)
(28593, "iso-8859-3"), # ISO 8859-3 Latin 3
(28594, "iso-8859-4"), # ISO 8859-4 Baltic
(28595, "iso-8859-5"), # ISO 8859-5 Cyrillic
(28596, "iso-8859-6"), # ISO 8859-6 Arabic
(28597, "iso-8859-7"), # ISO 8859-7 Greek
(28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
(28599, "iso-8859-9"), # ISO 8859-9 Turkish
(28603, "iso-8859-13"), # ISO 8859-13 Estonian
(28605, "iso-8859-15"), # ISO 8859-15 Latin 9
(29001, "x-Europa"), # Europa 3
(38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
(50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
(50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
(50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
(50225, "iso-2022-kr"), # ISO 2022 Korean
(50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
(50229, ""), # ISO 2022 Traditional Chinese
(50930, ""), # EBCDIC Japanese (Katakana) Extended
(50931, ""), # EBCDIC US-Canada and Japanese
(50933, ""), # EBCDIC Korean Extended and Korean
(50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese
(50936, ""), # EBCDIC Simplified Chinese
(50937, ""), # EBCDIC US-Canada and Traditional Chinese
(50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese
(51932, "euc-jp"), # EUC Japanese
(51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC)
(51949, "euc-kr"), # EUC Korean
(51950, ""), # EUC Traditional Chinese
(52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
(54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
(57002, "x-iscii-de"), # ISCII Devanagari
(57003, "x-iscii-be"), # ISCII Bengali
(57004, "x-iscii-ta"), # ISCII Tamil
(57005, "x-iscii-te"), # ISCII Telugu
(57006, "x-iscii-as"), # ISCII Assamese
(57007, "x-iscii-or"), # ISCII Oriya
(57008, "x-iscii-ka"), # ISCII Kannada
(57009, "x-iscii-ma"), # ISCII Malayalam
(57010, "x-iscii-gu"), # ISCII Gujarati
(57011, "x-iscii-pa"), # ISCII Punjabi
(65000, "utf-7"), # Unicode (UTF-7)
(65001, "utf-8")] # Unicode (UTF-8)
when false:
# not needed yet:
type
@ -219,22 +219,22 @@ when defined(windows):
proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
proc nameToCodePage(name: string): CodePage =
var nameAsInt: int
if parseInt(name, nameAsInt) == 0: nameAsInt = -1
for no, na in items(winEncodings):
if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
result = CodePage(-1)
proc codePageToName(c: CodePage): string =
for no, na in items(winEncodings):
if no == int(c):
return if na.len != 0: na else: $no
result = ""
proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
proc multiByteToWideChar(
codePage: CodePage,
dwFlags: int32,
@ -254,7 +254,7 @@ when defined(windows):
lpDefaultChar: cstring=nil,
lpUsedDefaultChar: pointer=nil): cint {.
stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
else:
when defined(haiku):
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
@ -292,31 +292,31 @@ else:
proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer,
outbuf: var cstring, outbytesLeft: var int): int {.
importc: prefix & "iconv", cdecl, dynlib: iconvDll.}
proc getCurrentEncoding*(): string =
## retrieves the current encoding. On Unix, always "UTF-8" is returned.
when defined(windows):
result = codePageToName(getACP())
else:
result = "UTF-8"
proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
## opens a converter that can convert from `srcEncoding` to `destEncoding`.
## Raises `EIO` if it cannot fulfill the request.
when not defined(windows):
result = iconvOpen(destEncoding, srcEncoding)
if result == nil:
raise newException(EncodingError,
"cannot create encoding converter from " &
raise newException(EncodingError,
"cannot create encoding converter from " &
srcEncoding & " to " & destEncoding)
else:
result.dest = nameToCodePage(destEncoding)
result.src = nameToCodePage(srcEncoding)
if int(result.dest) == -1:
raise newException(EncodingError,
raise newException(EncodingError,
"cannot find encoding " & destEncoding)
if int(result.src) == -1:
raise newException(EncodingError,
raise newException(EncodingError,
"cannot find encoding " & srcEncoding)
proc close*(c: EncodingConverter) =
@ -328,7 +328,7 @@ when defined(windows):
proc convert*(c: EncodingConverter, s: string): string =
## converts `s` to `destEncoding` that was given to the converter `c`. It
## assumed that `s` is in `srcEncoding`.
# special case: empty string: needed because MultiByteToWideChar
# return 0 in case of error:
if s.len == 0: return ""
@ -336,21 +336,21 @@ when defined(windows):
var cap = s.len + s.len shr 2
result = newStringOfCap(cap*2)
# convert to utf-16 LE
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len),
lpWideCharStr = cstring(result),
cchWideChar = cint(cap))
if m == 0:
if m == 0:
# try again; ask for capacity:
cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len),
lpWideCharStr = nil,
cchWideChar = cint(0))
# and do the conversion properly:
result = newStringOfCap(cap*2)
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len),
lpWideCharStr = cstring(result),
@ -361,7 +361,7 @@ when defined(windows):
setLen(result, m*2)
else:
assert(false) # cannot happen
# if already utf-16 LE, no further need to do something:
if int(c.dest) == 1200: return
# otherwise the fun starts again:
@ -428,7 +428,7 @@ else:
outLen = len(result) - offset
else:
raiseOSError(lerr.OSErrorCode)
# iconv has a buffer that needs flushing, specially if the last char is
# iconv has a buffer that needs flushing, specially if the last char is
# not '\0'
discard iconv(c, nil, nil, dst, outLen)
if iconvres == cint(-1) and errno == E2BIG:
@ -441,7 +441,7 @@ else:
# trim output buffer
setLen(result, len(result) - outLen)
proc convert*(s: string, destEncoding = "UTF-8",
proc convert*(s: string, destEncoding = "UTF-8",
srcEncoding = "CP1252"): string =
## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`.
## This opens a converter, uses it and closes it again and is thus more

View file

@ -24,7 +24,7 @@ proc swapEndian64*(outp, inp: pointer) =
o[6] = i[1]
o[7] = i[0]
proc swapEndian32*(outp, inp: pointer) =
proc swapEndian32*(outp, inp: pointer) =
## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
## contain at least 4 bytes.
var i = cast[cstring](inp)
@ -34,7 +34,7 @@ proc swapEndian32*(outp, inp: pointer) =
o[2] = i[1]
o[3] = i[0]
proc swapEndian16*(outp, inp: pointer) =
proc swapEndian16*(outp, inp: pointer) =
## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
## contain at least 2 bytes.
var
@ -50,7 +50,7 @@ when system.cpuEndian == bigEndian:
proc bigEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
proc bigEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2)
else:
else:
proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2)

View file

@ -18,6 +18,6 @@ const magicIdentSeparatorRuneByteWidth* = 3
# Used by pure/hashes.nim, and the compiler parsing
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } =
result = cs[i] == '\226' and
result = cs[i] == '\226' and
cs[i + 1] == '\128' and
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen

View file

@ -10,7 +10,7 @@
## :Author: Alex Mitchell
##
## This module implements an event system that is not dependent on external
## graphical toolkits. It was originally called ``NimEE`` because
## graphical toolkits. It was originally called ``NimEE`` because
## it was inspired by Python's PyEE module. There are two ways you can use
## events: one is a python-inspired way; the other is more of a C-style way.
##
@ -23,7 +23,7 @@
## # Python way
## ee.on("EventName", handleevent)
## ee.emit("EventName", genericargs)
##
##
## # C/Java way
## # Declare a type
## type
@ -45,7 +45,7 @@ type
{.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 = @[]
@ -61,7 +61,7 @@ proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.
if fn == handler.handlers[i]:
handler.handlers.del(i)
break
proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool =
## Checks if a callback is registered to this event handler.
return handler.handlers.contains(fn)
@ -86,8 +86,8 @@ proc on*(emitter: var EventEmitter, event: string, fn: proc(e: EventArgs) {.clos
emitter.s.add(eh)
else:
addHandler(emitter.s[i], fn)
proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler,
proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler,
args: EventArgs) =
## Fires an event handler with specified event arguments.
for fn in items(eventhandler.handlers): fn(args)

View file

@ -30,7 +30,7 @@ type
fd: cint
handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
targets: Table[cint, string]
MonitorEventType* = enum ## Monitor event type
MonitorAccess, ## File was accessed.
MonitorAttrib, ## Metadata changed.
@ -44,7 +44,7 @@ type
MonitorMoved, ## File was moved.
MonitorOpen, ## File was opened.
MonitorAll ## Filter for all event types.
MonitorEvent* = object
case kind*: MonitorEventType ## Type of the event.
of MonitorMoveSelf, MonitorMoved:
@ -77,7 +77,7 @@ proc add*(monitor: FSMonitor, target: string,
## Adds ``target`` which may be a directory or a file to the list of
## watched paths of ``monitor``.
## You can specify the events to report using the ``filters`` parameter.
var INFilter = -1
for f in filters:
case f
@ -93,7 +93,7 @@ proc add*(monitor: FSMonitor, target: string,
of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO
of MonitorOpen: INFilter = INFilter and IN_OPEN
of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
if result < 0:
raiseOSError(osLastError())
@ -125,13 +125,13 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
mev.name = $cstr
else:
mev.name = ""
if (event.mask.int and IN_MOVED_FROM) != 0:
if (event.mask.int and IN_MOVED_FROM) != 0:
# Moved from event, add to m's collection
movedFrom.add(event.cookie.cint, (mev.wd, mev.name))
inc(i, sizeof(INotifyEvent) + event.len.int)
continue
elif (event.mask.int and IN_MOVED_TO) != 0:
elif (event.mask.int and IN_MOVED_TO) != 0:
mev.kind = MonitorMoved
assert movedFrom.hasKey(event.cookie.cint)
# Find the MovedFrom event.
@ -141,23 +141,23 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
movedFrom.del(event.cookie.cint)
elif (event.mask.int and IN_ACCESS) != 0: mev.kind = MonitorAccess
elif (event.mask.int and IN_ATTRIB) != 0: mev.kind = MonitorAttrib
elif (event.mask.int and IN_CLOSE_WRITE) != 0:
elif (event.mask.int and IN_CLOSE_WRITE) != 0:
mev.kind = MonitorCloseWrite
elif (event.mask.int and IN_CLOSE_NOWRITE) != 0:
elif (event.mask.int and IN_CLOSE_NOWRITE) != 0:
mev.kind = MonitorCloseNoWrite
elif (event.mask.int and IN_CREATE) != 0: mev.kind = MonitorCreate
elif (event.mask.int and IN_DELETE) != 0:
elif (event.mask.int and IN_DELETE) != 0:
mev.kind = MonitorDelete
elif (event.mask.int and IN_DELETE_SELF) != 0:
elif (event.mask.int and IN_DELETE_SELF) != 0:
mev.kind = MonitorDeleteSelf
elif (event.mask.int and IN_MODIFY) != 0: mev.kind = MonitorModify
elif (event.mask.int and IN_MOVE_SELF) != 0:
elif (event.mask.int and IN_MOVE_SELF) != 0:
mev.kind = MonitorMoveSelf
elif (event.mask.int and IN_OPEN) != 0: mev.kind = MonitorOpen
if mev.kind != MonitorMoved:
mev.fullname = ""
result.add(mev)
inc(i, sizeof(INotifyEvent) + event.len.int)
@ -211,7 +211,7 @@ when not defined(testing) and isMainModule:
echo("Name is ", ev.name)
else:
echo("Name ", ev.name, " fullname ", ev.fullName))
while true:
if not disp.poll(): break
main()

View file

@ -142,7 +142,7 @@ when false:
if meth == reqPost:
# get from client and post to CGI program:
var buf = alloc(contentLength)
if recv(client, buf, contentLength) != contentLength:
if recv(client, buf, contentLength) != contentLength:
dealloc(buf)
raiseOSError()
var inp = process.inputStream
@ -177,7 +177,7 @@ when false:
else:
path = "." & data[1]
# path starts with "/", by adding "." in front of it we serve files from cwd
if cmpIgnoreCase(data[0], "GET") == 0:
if q >= 0:
cgi = true
@ -218,12 +218,12 @@ type
headers*: StringTableRef ## headers with which the client made the request
body*: string ## only set with POST requests
ip*: string ## ip address of the requesting client
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
## acquired that can be accessed later by the ``port`` proc.
@ -262,7 +262,7 @@ proc next*(s: var Server) =
var data = ""
s.client.readLine(data)
if data == "":
# Socket disconnected
# Socket disconnected
s.client.close()
next(s)
return
@ -283,9 +283,9 @@ proc next*(s: var Server) =
s.client.close()
next(s)
return
var i = skipWhitespace(data)
if skipIgnoreCase(data, "GET") > 0:
if skipIgnoreCase(data, "GET") > 0:
s.reqMethod = "GET"
inc(i, 3)
elif skipIgnoreCase(data, "POST") > 0:
@ -296,7 +296,7 @@ proc next*(s: var Server) =
s.client.close()
next(s)
return
if s.reqMethod == "POST":
# Check for Expect header
if s.headers.hasKey("Expect"):
@ -304,7 +304,7 @@ proc next*(s: var Server) =
s.client.sendStatus("100 Continue")
else:
s.client.sendStatus("417 Expectation Failed")
# Read the body
# - Check for Content-length header
if s.headers.hasKey("Content-Length"):
@ -340,13 +340,13 @@ proc next*(s: var Server) =
s.client.close()
next(s)
return
var L = skipWhitespace(data, i)
inc(i, L)
# XXX we ignore "HTTP/1.1" etc. for now here
var query = 0
var last = i
while last < data.len and data[last] notin Whitespace:
while last < data.len and data[last] notin Whitespace:
if data[last] == '?' and query == 0: query = last
inc(last)
if query > 0:
@ -360,7 +360,7 @@ proc close*(s: Server) =
## closes the server (and the socket the server uses).
close(s.socket)
proc run*(handleRequest: proc (client: Socket,
proc run*(handleRequest: proc (client: Socket,
path, query: string): bool {.closure.},
port = Port(80)) =
## encapsulates the server object and main loop
@ -388,7 +388,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
var data = ""
s.client.readLine(data)
if data == "":
# Socket disconnected
# Socket disconnected
s.client.close()
return
var header = ""
@ -408,9 +408,9 @@ proc nextAsync(s: PAsyncHTTPServer) =
else:
s.client.close()
return
var i = skipWhitespace(data)
if skipIgnoreCase(data, "GET") > 0:
if skipIgnoreCase(data, "GET") > 0:
s.reqMethod = "GET"
inc(i, 3)
elif skipIgnoreCase(data, "POST") > 0:
@ -420,7 +420,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
unimplemented(s.client)
s.client.close()
return
if s.reqMethod == "POST":
# Check for Expect header
if s.headers.hasKey("Expect"):
@ -428,7 +428,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
s.client.sendStatus("100 Continue")
else:
s.client.sendStatus("417 Expectation Failed")
# Read the body
# - Check for Content-length header
if s.headers.hasKey("Content-Length"):
@ -460,13 +460,13 @@ proc nextAsync(s: PAsyncHTTPServer) =
badRequest(s.client)
s.client.close()
return
var L = skipWhitespace(data, i)
inc(i, L)
# XXX we ignore "HTTP/1.1" etc. for now here
var query = 0
var last = i
while last < data.len and data[last] notin Whitespace:
while last < data.len and data[last] notin Whitespace:
if data[last] == '?' and query == 0: query = last
inc(last)
if query > 0:
@ -476,7 +476,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
s.query = ""
s.path = data.substr(i, last-1)
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
path, query: string): bool {.closure, gcsafe.},
port = Port(80), address = "",
reuseAddr = false): PAsyncHTTPServer =
@ -492,14 +492,14 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Soc
if quit: capturedRet.asyncSocket.close()
if reuseAddr:
capturedRet.asyncSocket.setSockOpt(OptReuseAddr, true)
capturedRet.asyncSocket.bindAddr(port, address)
capturedRet.asyncSocket.listen()
if port == Port(0):
capturedRet.port = getSockName(capturedRet.asyncSocket)
else:
capturedRet.port = port
capturedRet.client = invalidSocket
capturedRet.reqMethod = ""
capturedRet.body = ""
@ -524,11 +524,11 @@ when not defined(testing) and isMainModule:
echo("httpserver running on port ", s.port)
while true:
next(s)
inc(counter)
s.client.send("Hello, Andreas, for the $#th time. $# ? $#" % [
$counter, s.path, s.query] & wwwNL)
close(s.client)
close(s)

View file

@ -18,15 +18,15 @@ include "system/inclrtl"
import parseutils, strutils
proc validEmailAddress*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidEmailAddress".} =
## returns true if `s` seems to be a valid e-mail address.
rtl, extern: "nsuValidEmailAddress".} =
## returns true if `s` seems to be a valid e-mail address.
## The checking also uses a domain list.
const
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
if s[i] notin chars or s[i] == '.': return false
while s[i] in chars:
while s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false
inc(i)
if s[i] != '@': return false
@ -34,9 +34,9 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
if 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)
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': 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)
@ -59,6 +59,6 @@ proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
when isMainModule:
doAssert "wuseldusel@codehome.com".validEmailAddress
{.pop.}

View file

@ -33,7 +33,7 @@ type
when defined(windows):
fHandle: int
mapHandle: int
mapHandle: int
else:
handle: cint
@ -131,7 +131,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
fail(osLastError(), "error opening file")
if newFileSize != -1:
var
var
sizeHigh = int32(newFileSize shr 32)
sizeLow = int32(newFileSize and 0xffffffff)
@ -177,7 +177,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
rollback()
if result.handle != 0: discard close(result.handle)
raiseOSError(errCode)
var flags = if readonly: O_RDONLY else: O_RDWR
if newFileSize != -1:
@ -221,7 +221,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
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.
var error = false
var lastErr: OSErrorCode
@ -245,7 +245,7 @@ proc close*(f: var MemFile) =
f.mapHandle = 0
else:
f.handle = 0
if error: raiseOSError(lastErr)
type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records

View file

@ -7,7 +7,7 @@ type
{.deprecated: [TMersenneTwister: MersenneTwister].}
proc newMersenneTwister*(seed: int): MersenneTwister =
proc newMersenneTwister*(seed: int): MersenneTwister =
result.index = 0
result.mt[0]= uint32(seed)
for i in 1..623'u32:

View file

@ -11,23 +11,23 @@ type OneVarFunction* = proc (x: float): float
{.deprecated: [TOneVarFunction: OneVarFunction].}
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
tuple[rootx, rooty: float, success: bool]=
## Searches `function` for a root between `xmin` and `xmax`
## Searches `function` for a root between `xmin` and `xmax`
## using brents method. If the function value at `xmin`and `xmax` has the
## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
## and succes is set to false.
## Otherwise there exists at least one root and success is set to true.
## This root is searched for at most `maxiter` iterations.
## If `tol` tolerance is reached within `maxiter` iterations
## If `tol` tolerance is reached within `maxiter` iterations
## the root refinement stops and success=true.
# see http://en.wikipedia.org/wiki/Brent%27s_method
var
var
a=xmin
b=xmax
c=a
d=1.0e308
d=1.0e308
fa=function(a)
fb=function(b)
fc=fa
@ -42,19 +42,19 @@ proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
return (a,fa,false)
else:
return (b,fb,false)
if abs(fa)<abs(fb):
swap(fa,fb)
swap(a,b)
while fb!=0.0 and abs(a-b)>tol:
if fa!=fc and fb!=fc: # inverse quadratic interpolation
s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
else: #secant rule
s = b - fb * (b - a) / (fb - fa)
tmp2 = (3.0 * a + b) / 4.0
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
(not mflag and abs(s - b) >= abs(c - d) / 2.0):
s=(a+b)/2.0
mflag=true
@ -80,5 +80,5 @@ proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
inc i
if i>maxiter:
break
return (b,fb,true)

View file

@ -8,7 +8,7 @@
#
## Nim OID support. An OID is a global ID that consists of a timestamp,
## a unique counter and a random value. This combination should suffice to
## a unique counter and a random value. This combination should suffice to
## produce a globally distributed unique ID. This implementation was extracted
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
##
@ -19,13 +19,13 @@ import times, endians
type
Oid* = object ## an OID
time: int32 ##
fuzz: int32 ##
count: int32 ##
time: int32 ##
fuzz: int32 ##
count: int32 ##
{.deprecated: [Toid: Oid].}
proc hexbyte*(hex: char): int =
proc hexbyte*(hex: char): int =
case hex
of '0'..'9': result = (ord(hex) - ord('0'))
of 'a'..'f': result = (ord(hex) - ord('a') + 10)
@ -40,7 +40,7 @@ proc parseOid*(str: cstring): Oid =
bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
inc(i)
proc oidToString*(oid: Oid, str: cstring) =
proc oidToString*(oid: Oid, str: cstring) =
const hex = "0123456789abcdef"
# work around a compiler bug:
var str = str
@ -59,7 +59,7 @@ proc `$`*(oid: Oid): string =
oidToString(oid, result)
var
incr: int
incr: int
fuzz: int32
proc genOid*(): Oid =
@ -69,10 +69,10 @@ proc genOid*(): Oid =
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
var t = gettime(nil)
var i = int32(incr)
atomicInc(incr)
if fuzz == 0:
# racy, but fine semantically:
srand(t)

View file

@ -7,17 +7,17 @@
# distribution, for details about the copyright.
#
## The ``parsecfg`` module implements a high performance configuration file
## parser. The configuration file's syntax is similar to the Windows ``.ini``
## format, but much more powerful, as it is not a line based parser. String
## literals, raw string literals and triple quoted string literals are supported
## The ``parsecfg`` module implements a high performance configuration file
## parser. The configuration file's syntax is similar to the Windows ``.ini``
## format, but much more powerful, as it is not a line based parser. String
## literals, raw string literals and triple quoted string literals are supported
## as in the Nim programming language.
## This is an example of how a configuration file may look like:
##
## .. include:: doc/mytest.cfg
## :literal:
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
## configuration file parser:
##
## .. code-block:: nim
@ -36,14 +36,14 @@ type
cfgKeyValuePair, ## a ``key=value`` pair has been detected
cfgOption, ## a ``--key=value`` command line option
cfgError ## an error occurred during parsing
CfgEvent* = object of RootObj ## describes a parsing event
case kind*: CfgEventKind ## the kind of the event
of cfgEof: nil
of cfgSectionStart:
section*: string ## `section` contains the name of the
of cfgSectionStart:
section*: string ## `section` contains the name of the
## parsed section start (syntax: ``[section]``)
of cfgKeyValuePair, cfgOption:
of cfgKeyValuePair, cfgOption:
key*, value*: string ## contains the (key, value) pair if an option
## of the form ``--key: value`` or an ordinary
## ``key= value`` pair has been parsed.
@ -52,14 +52,14 @@ type
of cfgError: ## the parser encountered an error: `msg`
msg*: string ## contains the error message. No exceptions
## are thrown if a parse error occurs.
TokKind = enum
tkInvalid, tkEof,
TokKind = enum
tkInvalid, tkEof,
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
Token = object # a token
kind: TokKind # the type of the token
literal: string # the parsed (string) literal
CfgParser* = object of BaseLexer ## the parser object.
tok: Token
filename: string
@ -69,12 +69,12 @@ type
# implementation
const
SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'}
const
SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'}
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
proc open*(c: var CfgParser, input: Stream, filename: string,
proc open*(c: var CfgParser, input: Stream, filename: string,
lineOffset = 0) {.rtl, extern: "npc$1".} =
## initializes the parser with an input stream. `Filename` is only used
## for nice error messages. `lineOffset` can be used to influence the line
@ -85,7 +85,7 @@ proc open*(c: var CfgParser, input: Stream, filename: string,
c.tok.literal = ""
inc(c.lineNumber, lineOffset)
rawGetTok(c, c.tok)
proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} =
## closes the parser `c` and its associated input stream.
lexbase.close(c)
@ -102,260 +102,260 @@ proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} =
## get the filename of the file that the parser processes.
result = c.filename
proc handleHexChar(c: var CfgParser, xi: var int) =
proc handleHexChar(c: var CfgParser, xi: var int) =
case c.buf[c.bufpos]
of '0'..'9':
of '0'..'9':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos)
of 'a'..'f':
of 'a'..'f':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
inc(c.bufpos)
of 'A'..'F':
of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos)
else:
else:
discard
proc handleDecChars(c: var CfgParser, xi: var int) =
while c.buf[c.bufpos] in {'0'..'9'}:
proc handleDecChars(c: var CfgParser, xi: var int) =
while c.buf[c.bufpos] in {'0'..'9'}:
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos)
proc getEscapedChar(c: var CfgParser, tok: var Token) =
proc getEscapedChar(c: var CfgParser, tok: var Token) =
inc(c.bufpos) # skip '\'
case c.buf[c.bufpos]
of 'n', 'N':
of 'n', 'N':
add(tok.literal, "\n")
inc(c.bufpos)
of 'r', 'R', 'c', 'C':
of 'r', 'R', 'c', 'C':
add(tok.literal, '\c')
inc(c.bufpos)
of 'l', 'L':
of 'l', 'L':
add(tok.literal, '\L')
inc(c.bufpos)
of 'f', 'F':
of 'f', 'F':
add(tok.literal, '\f')
inc(c.bufpos)
of 'e', 'E':
of 'e', 'E':
add(tok.literal, '\e')
inc(c.bufpos)
of 'a', 'A':
of 'a', 'A':
add(tok.literal, '\a')
inc(c.bufpos)
of 'b', 'B':
of 'b', 'B':
add(tok.literal, '\b')
inc(c.bufpos)
of 'v', 'V':
of 'v', 'V':
add(tok.literal, '\v')
inc(c.bufpos)
of 't', 'T':
of 't', 'T':
add(tok.literal, '\t')
inc(c.bufpos)
of '\'', '"':
of '\'', '"':
add(tok.literal, c.buf[c.bufpos])
inc(c.bufpos)
of '\\':
of '\\':
add(tok.literal, '\\')
inc(c.bufpos)
of 'x', 'X':
of 'x', 'X':
inc(c.bufpos)
var xi = 0
handleHexChar(c, xi)
handleHexChar(c, xi)
add(tok.literal, chr(xi))
of '0'..'9':
of '0'..'9':
var xi = 0
handleDecChars(c, xi)
if (xi <= 255): add(tok.literal, chr(xi))
else: tok.kind = tkInvalid
else: tok.kind = tkInvalid
proc handleCRLF(c: var CfgParser, pos: int): int =
proc handleCRLF(c: var CfgParser, pos: int): int =
case c.buf[pos]
of '\c': result = lexbase.handleCR(c, pos)
of '\L': result = lexbase.handleLF(c, pos)
else: result = pos
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
var pos = c.bufpos + 1 # skip "
var buf = c.buf # put `buf` in a register
tok.kind = tkSymbol
if (buf[pos] == '"') and (buf[pos + 1] == '"'):
if (buf[pos] == '"') and (buf[pos + 1] == '"'):
# long string literal:
inc(pos, 2) # skip ""
# skip leading newline:
pos = handleCRLF(c, pos)
buf = c.buf
while true:
while true:
case buf[pos]
of '"':
if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
of '"':
if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
add(tok.literal, '"')
inc(pos)
of '\c', '\L':
of '\c', '\L':
pos = handleCRLF(c, pos)
buf = c.buf
add(tok.literal, "\n")
of lexbase.EndOfFile:
of lexbase.EndOfFile:
tok.kind = tkInvalid
break
else:
break
else:
add(tok.literal, buf[pos])
inc(pos)
c.bufpos = pos + 3 # skip the three """
else:
else:
# ordinary string literal
while true:
while true:
var ch = buf[pos]
if ch == '"':
if ch == '"':
inc(pos) # skip '"'
break
if ch in {'\c', '\L', lexbase.EndOfFile}:
break
if ch in {'\c', '\L', lexbase.EndOfFile}:
tok.kind = tkInvalid
break
if (ch == '\\') and not rawMode:
break
if (ch == '\\') and not rawMode:
c.bufpos = pos
getEscapedChar(c, tok)
pos = c.bufpos
else:
else:
add(tok.literal, ch)
inc(pos)
c.bufpos = pos
proc getSymbol(c: var CfgParser, tok: var Token) =
proc getSymbol(c: var CfgParser, tok: var Token) =
var pos = c.bufpos
var buf = c.buf
while true:
while true:
add(tok.literal, buf[pos])
inc(pos)
if not (buf[pos] in SymChars): break
if not (buf[pos] in SymChars): break
c.bufpos = pos
tok.kind = tkSymbol
proc skip(c: var CfgParser) =
proc skip(c: var CfgParser) =
var pos = c.bufpos
var buf = c.buf
while true:
while true:
case buf[pos]
of ' ', '\t':
of ' ', '\t':
inc(pos)
of '#', ';':
of '#', ';':
while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
of '\c', '\L':
of '\c', '\L':
pos = handleCRLF(c, pos)
buf = c.buf
else:
else:
break # EndOfFile also leaves the loop
c.bufpos = pos
proc rawGetTok(c: var CfgParser, tok: var Token) =
proc rawGetTok(c: var CfgParser, tok: var Token) =
tok.kind = tkInvalid
setLen(tok.literal, 0)
skip(c)
case c.buf[c.bufpos]
of '=':
of '=':
tok.kind = tkEquals
inc(c.bufpos)
tok.literal = "="
of '-':
of '-':
inc(c.bufpos)
if c.buf[c.bufpos] == '-': inc(c.bufpos)
tok.kind = tkDashDash
tok.literal = "--"
of ':':
of ':':
tok.kind = tkColon
inc(c.bufpos)
tok.literal = ":"
of 'r', 'R':
if c.buf[c.bufpos + 1] == '\"':
of 'r', 'R':
if c.buf[c.bufpos + 1] == '\"':
inc(c.bufpos)
getString(c, tok, true)
else:
else:
getSymbol(c, tok)
of '[':
of '[':
tok.kind = tkBracketLe
inc(c.bufpos)
tok.literal = "]"
of ']':
of ']':
tok.kind = tkBracketRi
inc(c.bufpos)
tok.literal = "]"
of '"':
of '"':
getString(c, tok, false)
of lexbase.EndOfFile:
of lexbase.EndOfFile:
tok.kind = tkEof
tok.literal = "[EOF]"
else: getSymbol(c, tok)
proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formated error message containing current line and
## column information.
result = `%`("$1($2, $3) Error: $4",
result = `%`("$1($2, $3) Error: $4",
[c.filename, $getLine(c), $getColumn(c), msg])
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formated warning message containing current line and
## column information.
result = `%`("$1($2, $3) Warning: $4",
result = `%`("$1($2, $3) Warning: $4",
[c.filename, $getLine(c), $getColumn(c), msg])
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
## returns a properly formated warning message containing that
## an entry is ignored.
case e.kind
case e.kind
of cfgSectionStart: result = c.warningStr("section ignored: " & e.section)
of cfgKeyValuePair: result = c.warningStr("key ignored: " & e.key)
of cfgOption:
of cfgOption:
result = c.warningStr("command ignored: " & e.key & ": " & e.value)
of cfgError: result = e.msg
of cfgEof: result = ""
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
if c.tok.kind == tkSymbol:
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
if c.tok.kind == tkSymbol:
result.kind = kind
result.key = c.tok.literal
result.value = ""
rawGetTok(c, c.tok)
if c.tok.kind in {tkEquals, tkColon}:
if c.tok.kind in {tkEquals, tkColon}:
rawGetTok(c, c.tok)
if c.tok.kind == tkSymbol:
if c.tok.kind == tkSymbol:
result.value = c.tok.literal
else:
else:
reset result
result.kind = cfgError
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
rawGetTok(c, c.tok)
else:
else:
result.kind = cfgError
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
rawGetTok(c, c.tok)
proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
## retrieves the first/next event. This controls the parser.
case c.tok.kind
of tkEof:
case c.tok.kind
of tkEof:
result.kind = cfgEof
of tkDashDash:
of tkDashDash:
rawGetTok(c, c.tok)
result = getKeyValPair(c, cfgOption)
of tkSymbol:
of tkSymbol:
result = getKeyValPair(c, cfgKeyValuePair)
of tkBracketLe:
of tkBracketLe:
rawGetTok(c, c.tok)
if c.tok.kind == tkSymbol:
if c.tok.kind == tkSymbol:
result.kind = cfgSectionStart
result.section = c.tok.literal
else:
else:
result.kind = cfgError
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
rawGetTok(c, c.tok)
if c.tok.kind == tkBracketRi:
if c.tok.kind == tkBracketRi:
rawGetTok(c, c.tok)
else:
reset(result)
result.kind = cfgError
result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal)
of tkInvalid, tkEquals, tkColon, tkBracketRi:
of tkInvalid, tkEquals, tkColon, tkBracketRi:
result.kind = cfgError
result.msg = errorStr(c, "invalid token: " & c.tok.literal)
rawGetTok(c, c.tok)

View file

@ -8,7 +8,7 @@
#
## This module implements a simple high performance `CSV`:idx:
## (`comma separated value`:idx:) parser.
## (`comma separated value`:idx:) parser.
##
## Example: How to use the parser
## ==============================
@ -43,7 +43,7 @@ type
{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].}
proc raiseEInvalidCsv(filename: string, line, col: int,
proc raiseEInvalidCsv(filename: string, line, col: int,
msg: string) {.noreturn.} =
var e: ref CsvError
new(e)
@ -60,13 +60,13 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
## for nice error messages. The parser's behaviour can be controlled by
## the diverse optional parameters:
## - `separator`: character used to separate fields
## - `quote`: Used to quote fields containing special characters like
## - `quote`: Used to quote fields containing special characters like
## `separator`, `quote` or new-line characters. '\0' disables the parsing
## of quotes.
## - `escape`: removes any special meaning from the following character;
## - `escape`: removes any special meaning from the following character;
## '\0' disables escaping; if escaping is disabled and `quote` is not '\0',
## two `quote` characters are parsed one literal `quote` character.
## - `skipInitialSpace`: If true, whitespace immediately following the
## - `skipInitialSpace`: If true, whitespace immediately following the
## `separator` is ignored.
lexbase.open(my, input)
my.filename = filename
@ -77,13 +77,13 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
my.row = @[]
my.currRow = 0
proc parseField(my: var CsvParser, a: var string) =
proc parseField(my: var CsvParser, a: var string) =
var pos = my.bufpos
var buf = my.buf
if my.skipWhite:
while buf[pos] in {' ', '\t'}: inc(pos)
setLen(a, 0) # reuse memory
if buf[pos] == my.quote and my.quote != '\0':
if buf[pos] == my.quote and my.quote != '\0':
inc(pos)
while true:
var c = buf[pos]
@ -91,7 +91,7 @@ proc parseField(my: var CsvParser, a: var string) =
my.bufpos = pos # can continue after exception?
error(my, pos, my.quote & " expected")
break
elif c == my.quote:
elif c == my.quote:
if my.esc == '\0' and buf[pos+1] == my.quote:
add(a, my.quote)
inc(pos, 2)
@ -103,11 +103,11 @@ proc parseField(my: var CsvParser, a: var string) =
inc(pos, 2)
else:
case c
of '\c':
of '\c':
pos = handleCR(my, pos)
buf = my.buf
add(a, "\n")
of '\l':
of '\l':
pos = handleLF(my, pos)
buf = my.buf
add(a, "\n")
@ -123,11 +123,11 @@ proc parseField(my: var CsvParser, a: var string) =
inc(pos)
my.bufpos = pos
proc processedRows*(my: var CsvParser): int =
proc processedRows*(my: var CsvParser): int =
## returns number of the processed rows
return my.currRow
proc readRow*(my: var CsvParser, columns = 0): bool =
proc readRow*(my: var CsvParser, columns = 0): bool =
## reads the next row; if `columns` > 0, it expects the row to have
## exactly this many columns. Returns false if the end of the file
## has been encountered else true.
@ -140,13 +140,13 @@ proc readRow*(my: var CsvParser, columns = 0): bool =
my.row[col] = ""
parseField(my, my.row[col])
inc(col)
if my.buf[my.bufpos] == my.sep:
if my.buf[my.bufpos] == my.sep:
inc(my.bufpos)
else:
case my.buf[my.bufpos]
of '\c', '\l':
of '\c', '\l':
# skip empty lines:
while true:
while true:
case my.buf[my.bufpos]
of '\c': my.bufpos = handleCR(my, my.bufpos)
of '\l': my.bufpos = handleLF(my, my.bufpos)
@ -154,15 +154,15 @@ proc readRow*(my: var CsvParser, columns = 0): bool =
of '\0': discard
else: error(my, my.bufpos, my.sep & " expected")
break
setLen(my.row, col)
result = col > 0
if result and col != columns and columns > 0:
error(my, oldpos+1, $columns & " columns expected, but found " &
if result and col != columns and columns > 0:
error(my, oldpos+1, $columns & " columns expected, but found " &
$col & " columns")
inc(my.currRow)
proc close*(my: var CsvParser) {.inline.} =
proc close*(my: var CsvParser) {.inline.} =
## closes the parser `my` and its associated input stream.
lexbase.close(my)

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## The ``parsesql`` module implements a high performance SQL file
## The ``parsesql`` module implements a high performance SQL file
## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
import
@ -37,11 +37,11 @@ type
tkBracketLe, ## '['
tkBracketRi, ## ']'
tkDot ## '.'
Token = object # a token
kind: TokKind # the type of the token
literal: string # the parsed (string) literal
SqlLexer* = object of BaseLexer ## the parser object.
filename: string
@ -55,82 +55,82 @@ const
";", ":", ",", "(", ")", "[", "]", "."
]
proc open(L: var SqlLexer, input: Stream, filename: string) =
proc open(L: var SqlLexer, input: Stream, filename: string) =
lexbase.open(L, input)
L.filename = filename
proc close(L: var SqlLexer) =
proc close(L: var SqlLexer) =
lexbase.close(L)
proc getColumn(L: SqlLexer): int =
proc getColumn(L: SqlLexer): int =
## get the current column the parser has arrived at.
result = getColNumber(L, L.bufpos)
proc getLine(L: SqlLexer): int =
proc getLine(L: SqlLexer): int =
result = L.lineNumber
proc handleHexChar(c: var SqlLexer, xi: var int) =
proc handleHexChar(c: var SqlLexer, xi: var int) =
case c.buf[c.bufpos]
of '0'..'9':
of '0'..'9':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos)
of 'a'..'f':
of 'a'..'f':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
inc(c.bufpos)
of 'A'..'F':
of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos)
else:
else:
discard
proc handleOctChar(c: var SqlLexer, xi: var int) =
proc handleOctChar(c: var SqlLexer, xi: var int) =
if c.buf[c.bufpos] in {'0'..'7'}:
xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos)
proc getEscapedChar(c: var SqlLexer, tok: var Token) =
proc getEscapedChar(c: var SqlLexer, tok: var Token) =
inc(c.bufpos)
case c.buf[c.bufpos]
of 'n', 'N':
of 'n', 'N':
add(tok.literal, '\L')
inc(c.bufpos)
of 'r', 'R', 'c', 'C':
of 'r', 'R', 'c', 'C':
add(tok.literal, '\c')
inc(c.bufpos)
of 'l', 'L':
of 'l', 'L':
add(tok.literal, '\L')
inc(c.bufpos)
of 'f', 'F':
of 'f', 'F':
add(tok.literal, '\f')
inc(c.bufpos)
of 'e', 'E':
of 'e', 'E':
add(tok.literal, '\e')
inc(c.bufpos)
of 'a', 'A':
of 'a', 'A':
add(tok.literal, '\a')
inc(c.bufpos)
of 'b', 'B':
of 'b', 'B':
add(tok.literal, '\b')
inc(c.bufpos)
of 'v', 'V':
of 'v', 'V':
add(tok.literal, '\v')
inc(c.bufpos)
of 't', 'T':
of 't', 'T':
add(tok.literal, '\t')
inc(c.bufpos)
of '\'', '\"':
of '\'', '\"':
add(tok.literal, c.buf[c.bufpos])
inc(c.bufpos)
of '\\':
of '\\':
add(tok.literal, '\\')
inc(c.bufpos)
of 'x', 'X':
of 'x', 'X':
inc(c.bufpos)
var xi = 0
handleHexChar(c, xi)
handleHexChar(c, xi)
add(tok.literal, chr(xi))
of '0'..'7':
of '0'..'7':
var xi = 0
handleOctChar(c, xi)
handleOctChar(c, xi)
@ -138,20 +138,20 @@ proc getEscapedChar(c: var SqlLexer, tok: var Token) =
if (xi <= 255): add(tok.literal, chr(xi))
else: tok.kind = tkInvalid
else: tok.kind = tkInvalid
proc handleCRLF(c: var SqlLexer, pos: int): int =
proc handleCRLF(c: var SqlLexer, pos: int): int =
case c.buf[pos]
of '\c': result = lexbase.handleCR(c, pos)
of '\L': result = lexbase.handleLF(c, pos)
else: result = pos
proc skip(c: var SqlLexer) =
proc skip(c: var SqlLexer) =
var pos = c.bufpos
var buf = c.buf
var nested = 0
while true:
while true:
case buf[pos]
of ' ', '\t':
of ' ', '\t':
inc(pos)
of '-':
if buf[pos+1] == '-':
@ -164,7 +164,7 @@ proc skip(c: var SqlLexer) =
while true:
case buf[pos]
of '\0': break
of '\c', '\L':
of '\c', '\L':
pos = handleCRLF(c, pos)
buf = c.buf
of '*':
@ -182,20 +182,20 @@ proc skip(c: var SqlLexer) =
inc(pos)
else: inc(pos)
else: break
of '\c', '\L':
of '\c', '\L':
pos = handleCRLF(c, pos)
buf = c.buf
else:
else:
break # EndOfFile also leaves the loop
c.bufpos = pos
proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
var pos = c.bufpos + 1
var buf = c.buf
tok.kind = kind
block parseLoop:
while true:
while true:
while true:
var ch = buf[pos]
if ch == '\'':
if buf[pos+1] == '\'':
@ -203,15 +203,15 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
add(tok.literal, '\'')
else:
inc(pos)
break
elif ch in {'\c', '\L', lexbase.EndOfFile}:
break
elif ch in {'\c', '\L', lexbase.EndOfFile}:
tok.kind = tkInvalid
break parseLoop
elif (ch == '\\') and kind == tkEscapeConstant:
elif (ch == '\\') and kind == tkEscapeConstant:
c.bufpos = pos
getEscapedChar(c, tok)
pos = c.bufpos
else:
else:
add(tok.literal, ch)
inc(pos)
c.bufpos = pos
@ -227,7 +227,7 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
else: break parseLoop
c.bufpos = pos
proc getDollarString(c: var SqlLexer, tok: var Token) =
proc getDollarString(c: var SqlLexer, tok: var Token) =
var pos = c.bufpos + 1
var buf = c.buf
tok.kind = tkDollarQuotedConstant
@ -241,7 +241,7 @@ proc getDollarString(c: var SqlLexer, tok: var Token) =
return
while true:
case buf[pos]
of '\c', '\L':
of '\c', '\L':
pos = handleCRLF(c, pos)
buf = c.buf
add(tok.literal, "\L")
@ -263,10 +263,10 @@ proc getDollarString(c: var SqlLexer, tok: var Token) =
inc(pos)
c.bufpos = pos
proc getSymbol(c: var SqlLexer, tok: var Token) =
proc getSymbol(c: var SqlLexer, tok: var Token) =
var pos = c.bufpos
var buf = c.buf
while true:
while true:
add(tok.literal, buf[pos])
inc(pos)
if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}:
@ -274,7 +274,7 @@ proc getSymbol(c: var SqlLexer, tok: var Token) =
c.bufpos = pos
tok.kind = tkIdentifier
proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
var pos = c.bufpos + 1
var buf = c.buf
tok.kind = tkQuotedIdentifier
@ -287,7 +287,7 @@ proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
else:
inc(pos)
break
elif ch in {'\c', '\L', lexbase.EndOfFile}:
elif ch in {'\c', '\L', lexbase.EndOfFile}:
tok.kind = tkInvalid
break
else:
@ -300,15 +300,15 @@ proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) =
var buf = c.buf
block parseLoop:
while true:
while true:
while true:
var ch = buf[pos]
if ch in validChars:
add(tok.literal, ch)
inc(pos)
inc(pos)
elif ch == '\'':
inc(pos)
break
else:
else:
tok.kind = tkInvalid
break parseLoop
c.bufpos = pos
@ -353,7 +353,7 @@ proc getNumeric(c: var SqlLexer, tok: var Token) =
inc(pos)
else:
tok.kind = tkInvalid
c.bufpos = pos
c.bufpos = pos
proc getOperator(c: var SqlLexer, tok: var Token) =
const operators = {'+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%',
@ -381,12 +381,12 @@ proc getOperator(c: var SqlLexer, tok: var Token) =
inc(pos)
c.bufpos = pos
proc getTok(c: var SqlLexer, tok: var Token) =
proc getTok(c: var SqlLexer, tok: var Token) =
tok.kind = tkInvalid
setLen(tok.literal, 0)
skip(c)
case c.buf[c.bufpos]
of ';':
of ';':
tok.kind = tkSemicolon
inc(c.bufpos)
add(tok.literal, ';')
@ -394,15 +394,15 @@ proc getTok(c: var SqlLexer, tok: var Token) =
tok.kind = tkComma
inc(c.bufpos)
add(tok.literal, ',')
of ':':
of ':':
tok.kind = tkColon
inc(c.bufpos)
add(tok.literal, ':')
of 'e', 'E':
if c.buf[c.bufpos + 1] == '\'':
of 'e', 'E':
if c.buf[c.bufpos + 1] == '\'':
inc(c.bufpos)
getString(c, tok, tkEscapeConstant)
else:
else:
getSymbol(c, tok)
of 'b', 'B':
if c.buf[c.bufpos + 1] == '\'':
@ -417,11 +417,11 @@ proc getTok(c: var SqlLexer, tok: var Token) =
else:
getSymbol(c, tok)
of '$': getDollarString(c, tok)
of '[':
of '[':
tok.kind = tkBracketLe
inc(c.bufpos)
add(tok.literal, '[')
of ']':
of ']':
tok.kind = tkBracketRi
inc(c.bufpos)
add(tok.literal, ']')
@ -433,7 +433,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
tok.kind = tkParRi
inc(c.bufpos)
add(tok.literal, ')')
of '.':
of '.':
if c.buf[c.bufpos + 1] in Digits:
getNumeric(c, tok)
else:
@ -443,7 +443,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
of '0'..'9': getNumeric(c, tok)
of '\'': getString(c, tok, tkStringConstant)
of '"': getQuotedIdentifier(c, tok)
of lexbase.EndOfFile:
of lexbase.EndOfFile:
tok.kind = tkEof
tok.literal = "[EOF]"
of 'a', 'c', 'd', 'f'..'w', 'y', 'z', 'A', 'C', 'D', 'F'..'W', 'Y', 'Z', '_',
@ -455,8 +455,8 @@ proc getTok(c: var SqlLexer, tok: var Token) =
else:
add(tok.literal, c.buf[c.bufpos])
inc(c.bufpos)
proc errorStr(L: SqlLexer, msg: string): string =
proc errorStr(L: SqlLexer, msg: string): string =
result = "$1($2, $3) Error: $4" % [L.filename, $getLine(L), $getColumn(L), msg]
@ -496,7 +496,7 @@ type
nkPrimaryKey,
nkForeignKey,
nkNotNull,
nkStmtList,
nkDot,
nkDotDot,
@ -530,13 +530,13 @@ type
nkValueList,
nkWhere,
nkCreateTable,
nkCreateTableIfNotExists,
nkCreateTableIfNotExists,
nkCreateType,
nkCreateTypeIfNotExists,
nkCreateIndex,
nkCreateIndexIfNotExists,
nkEnumDef
type
SqlParseError* = object of ValueError ## Invalid SQL encountered
SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
@ -563,11 +563,11 @@ proc newNode(k: SqlNodeKind, s: string): SqlNode =
new(result)
result.kind = k
result.strVal = s
proc len*(n: SqlNode): int =
if isNil(n.sons): result = 0
else: result = n.sons.len
proc add*(father, n: SqlNode) =
if isNil(father.sons): father.sons = @[]
add(father.sons, n)
@ -641,11 +641,11 @@ proc parseDataType(p: var SqlParser): SqlNode =
getTok(p)
eat(p, tkParRi)
proc getPrecedence(p: SqlParser): int =
proc getPrecedence(p: SqlParser): int =
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
result = 6
elif isOpr(p, "+") or isOpr(p, "-"):
result = 5
result = 5
elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or
isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or
isKeyw(p, "like"):
@ -664,7 +664,7 @@ proc parseExpr(p: var SqlParser): SqlNode
proc identOrLiteral(p: var SqlParser): SqlNode =
case p.tok.kind
of tkIdentifier, tkQuotedIdentifier:
of tkIdentifier, tkQuotedIdentifier:
result = newNode(nkIdent, p.tok.literal)
getTok(p)
of tkStringConstant, tkEscapeConstant, tkDollarQuotedConstant:
@ -686,21 +686,21 @@ proc identOrLiteral(p: var SqlParser): SqlNode =
getTok(p)
result = parseExpr(p)
eat(p, tkParRi)
else:
else:
sqlError(p, "expression expected")
getTok(p) # we must consume a token here to prevend endless loops!
proc primary(p: var SqlParser): SqlNode =
if p.tok.kind == tkOperator or isKeyw(p, "not"):
proc primary(p: var SqlParser): SqlNode =
if p.tok.kind == tkOperator or isKeyw(p, "not"):
result = newNode(nkPrefix)
result.add(newNode(nkIdent, p.tok.literal))
getTok(p)
result.add(primary(p))
return
result = identOrLiteral(p)
while true:
while true:
case p.tok.kind
of tkParLe:
of tkParLe:
var a = result
result = newNode(nkCall)
result.add(a)
@ -710,7 +710,7 @@ proc primary(p: var SqlParser): SqlNode =
if p.tok.kind == tkComma: getTok(p)
else: break
eat(p, tkParRi)
of tkDot:
of tkDot:
getTok(p)
var a = result
if p.tok.kind == tkDot:
@ -727,14 +727,14 @@ proc primary(p: var SqlParser): SqlNode =
sqlError(p, "identifier expected")
getTok(p)
else: break
proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
var
v2, node, opNode: SqlNode
v = primary(p) # expand while operators have priorities higher than 'limit'
var opPred = getPrecedence(p)
result = opPred
while opPred > limit:
while opPred > limit:
node = newNode(nkInfix)
opNode = newNode(nkIdent, p.tok.literal)
getTok(p)
@ -744,8 +744,8 @@ proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
node.add(v2)
v = node
opPred = getPrecedence(p)
proc parseExpr(p: var SqlParser): SqlNode =
proc parseExpr(p: var SqlParser): SqlNode =
discard lowestExprAux(p, result, - 1)
proc parseTableName(p: var SqlParser): SqlNode =
@ -765,7 +765,7 @@ proc parseColumnReference(p: var SqlParser): SqlNode =
result.add(parseTableName(p))
eat(p, tkParRi)
proc parseCheck(p: var SqlParser): SqlNode =
proc parseCheck(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkCheck)
result.add(parseExpr(p))
@ -817,9 +817,9 @@ proc parseColumnDef(p: var SqlParser): SqlNode =
result.add(newNode(nkIdent, p.tok.literal))
getTok(p)
result.add(parseDataType(p))
parseColumnConstraints(p, result)
parseColumnConstraints(p, result)
proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
getTok(p)
if isKeyw(p, "if"):
getTok(p)
@ -880,7 +880,7 @@ proc parseTableDef(p: var SqlParser): SqlNode =
result.add(parseTableConstraint(p))
if p.tok.kind != tkComma: break
eat(p, tkParRi)
proc parseTypeDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateType)
expectIdent(p)
@ -966,7 +966,7 @@ proc parseUpdate(p: var SqlParser): SqlNode =
result.add(parseWhere(p))
else:
result.add(nil)
proc parseDelete(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkDelete)
@ -1018,14 +1018,14 @@ proc parseSelect(p: var SqlParser): SqlNode =
while true:
getTok(p)
h.add(parseExpr(p))
if p.tok.kind != tkComma: break
if p.tok.kind != tkComma: break
result.add(h)
if isKeyw(p, "union"):
result.add(newNode(nkUnion))
getTok(p)
elif isKeyw(p, "intersect"):
result.add(newNode(nkIntersect))
getTok(p)
getTok(p)
elif isKeyw(p, "except"):
result.add(newNode(nkExcept))
getTok(p)
@ -1083,7 +1083,7 @@ proc open(p: var SqlParser, input: Stream, filename: string) =
p.tok.kind = tkInvalid
p.tok.literal = ""
getTok(p)
proc parse(p: var SqlParser): SqlNode =
## parses the content of `p`'s input stream and returns the SQL AST.
## Syntax errors raise an `EInvalidSql` exception.
@ -1094,13 +1094,13 @@ proc parse(p: var SqlParser): SqlNode =
result.add(s)
if result.len == 1:
result = result.sons[0]
proc close(p: var SqlParser) =
## closes the parser `p`. The associated input stream is closed too.
close(SqlLexer(p))
proc parseSQL*(input: Stream, filename: string): SqlNode =
## parses the SQL from `input` into an AST and returns the AST.
## parses the SQL from `input` into an AST and returns the AST.
## `filename` is only used for error messages.
## Syntax errors raise an `EInvalidSql` exception.
var p: SqlParser
@ -1114,7 +1114,7 @@ proc ra(n: SqlNode, s: var string, indent: int)
proc rs(n: SqlNode, s: var string, indent: int,
prefix = "(", suffix = ")",
sep = ", ") =
sep = ", ") =
if n.len > 0:
s.add(prefix)
for i in 0 .. n.len-1:
@ -1162,7 +1162,7 @@ proc ra(n: SqlNode, s: var string, indent: int) =
ra(n.sons[1], s, indent)
s.add(')')
of nkInfix:
s.add('(')
s.add('(')
ra(n.sons[1], s, indent)
s.add(' ')
ra(n.sons[0], s, indent)
@ -1207,13 +1207,13 @@ proc ra(n: SqlNode, s: var string, indent: int) =
s.add("insert into ")
ra(n.sons[0], s, indent)
ra(n.sons[1], s, indent)
if n.sons[2].kind == nkDefault:
if n.sons[2].kind == nkDefault:
s.add("default values")
else:
s.add("\nvalues ")
ra(n.sons[2], s, indent)
s.add(';')
of nkUpdate:
of nkUpdate:
s.add("update ")
ra(n.sons[0], s, indent)
s.add(" set ")
@ -1225,7 +1225,7 @@ proc ra(n: SqlNode, s: var string, indent: int) =
ra(it, s, indent)
ra(n.sons[L-1], s, indent)
s.add(';')
of nkDelete:
of nkDelete:
s.add("delete from ")
ra(n.sons[0], s, indent)
ra(n.sons[1], s, indent)
@ -1237,12 +1237,12 @@ proc ra(n: SqlNode, s: var string, indent: int) =
rs(n.sons[0], s, indent, "", "", ", ")
for i in 1 .. n.len-1: ra(n.sons[i], s, indent)
s.add(';')
of nkSelectColumns:
of nkSelectColumns:
assert(false)
of nkAsgn:
ra(n.sons[0], s, indent)
s.add(" = ")
ra(n.sons[1], s, indent)
ra(n.sons[1], s, indent)
of nkFrom:
s.add("\nfrom ")
rs(n, s, indent, "", "", ", ")
@ -1306,23 +1306,23 @@ proc ra(n: SqlNode, s: var string, indent: int) =
s.add("enum ")
rs(n, s, indent)
# What I want:
# What I want:
#
#select(columns = [T1.all, T2.name],
#select(columns = [T1.all, T2.name],
# fromm = [T1, T2],
# where = T1.name ==. T2.name,
# orderby = [name]):
#
#for row in dbQuery(db, """select x, y, z
# from a, b
#
#for row in dbQuery(db, """select x, y, z
# from a, b
# where a.name = b.name"""):
#
#
#select x, y, z:
# fromm: Table1, Table2
# where: x.name == y.name
#db.select(fromm = [t1, t2], where = t1.name == t2.name):
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
# writeLine x, y, z
proc renderSQL*(n: SqlNode): string =
@ -1338,7 +1338,7 @@ when not defined(testing) and isMainModule:
happiness happiness
);
CREATE INDEX table1_attr1 ON table1(attr1);
SELECT * FROM myTab WHERE col1 = 'happy';
"""), "stdin")))

View file

@ -19,7 +19,7 @@ import strutils
type
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
## any optional component is "" if it does not exist
scheme, username, password,
scheme, username, password,
hostname, port, path, query, anchor: string]
{.deprecated: [TUrl: Url].}
@ -31,7 +31,7 @@ proc parseUrl*(url: string): Url {.deprecated.} =
var hostname, port, path, query, anchor: string = ""
var temp = ""
if url[i] != '/': # url isn't a relative path
while true:
# Scheme
@ -48,7 +48,7 @@ proc parseUrl*(url: string): Url {.deprecated.} =
password = username.substr(colon+1)
username = username.substr(0, colon-1)
temp.setLen(0)
inc(i) #Skip the @
inc(i) #Skip the @
# hostname(subdomain, domain, port)
if url[i] == '/' or url[i] == '\0':
hostname = temp
@ -56,10 +56,10 @@ proc parseUrl*(url: string): Url {.deprecated.} =
if colon >= 0:
port = hostname.substr(colon+1)
hostname = hostname.substr(0, colon-1)
temp.setLen(0)
break
temp.add(url[i])
inc(i)
@ -75,7 +75,7 @@ proc parseUrl*(url: string): Url {.deprecated.} =
else:
path = temp
temp.setLen(0)
if url[i] == '\0':
if temp[0] == '?':
query = temp
@ -84,10 +84,10 @@ proc parseUrl*(url: string): Url {.deprecated.} =
else:
path = temp
break
temp.add(url[i])
inc(i)
return (scheme, username, password, hostname, port, path, query, anchor)
proc `$`*(u: Url): string {.deprecated.} =
@ -103,12 +103,12 @@ proc `$`*(u: Url): string {.deprecated.} =
result.add(u.password)
result.add("@")
result.add(u.hostname)
if u.port.len > 0:
if u.port.len > 0:
result.add(":")
result.add(u.port)
if u.path.len > 0:
if u.path.len > 0:
result.add("/")
result.add(u.path)
result.add(u.query)
result.add(u.anchor)

View file

@ -11,7 +11,7 @@ import math
import strutils
import numeric
type
type
Poly* = object
cofs:seq[float]
@ -40,7 +40,7 @@ proc `[]` *(p:Poly;idx:int):float=
if idx<0 or idx>p.degree:
return 0.0
return p.cofs[idx]
proc `[]=` *(p:var Poly;idx:int,v:float)=
## Sets an coefficient of the polynomial by index.
## p[2] set the quadric term, p[3] the cubic etc.
@ -55,15 +55,15 @@ proc `[]=` *(p:var Poly;idx:int,v:float)=
p.cofs[q]=0.0 #new-grown coefficients set to zero
p.cofs[idx]=v
iterator items*(p:Poly):float=
## Iterates through the coefficients of the polynomial.
var i=p.degree
while i>=0:
yield p[i]
dec i
dec i
proc clean*(p:var Poly;zerotol=0.0)=
## Removes leading zero coefficients of the polynomial.
## An optional tolerance can be given for what's considered zero.
@ -77,19 +77,19 @@ proc clean*(p:var Poly;zerotol=0.0)=
if relen: p.cofs.setLen(n+1)
proc `$` *(p:Poly):string =
proc `$` *(p:Poly):string =
## Gets a somewhat reasonable string representation of the polynomial
## The format should be compatible with most online function plotters,
## for example directly in google search
result=""
var first=true #might skip + sign if first coefficient
for idx in countdown(p.degree,0):
let a=p[idx]
if a==0.0:
continue
if a>= 0.0 and not first:
result.add('+')
first=false
@ -103,14 +103,14 @@ proc `$` *(p:Poly):string =
if result=="":
result="0"
proc derivative*(p: Poly): Poly=
## Returns a new polynomial, which is the derivative of `p`
newSeq[float](result.cofs,p.degree)
for idx in 0..high(result.cofs):
result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
proc diff*(p:Poly,x:float):float=
## Evaluates the differentiation of a polynomial with
## respect to `x` quickly using a modifed Horners method
@ -128,7 +128,7 @@ proc integral*(p:Poly):Poly=
result.cofs[0]=0.0 #constant arbitrary term, use 0.0
for i in 1..high(result.cofs):
result.cofs[i]=p.cofs[i-1]/float(i)
proc integrate*(p:Poly;xmin,xmax:float):float=
## Computes the definite integral of `p` between `xmin` and `xmax`
@ -145,9 +145,9 @@ proc integrate*(p:Poly;xmin,xmax:float):float=
s1 = s1*xmin+fac
s2 = s2*xmax+fac
dec n
result=s2*xmax-s1*xmin
proc initPoly*(cofs:varargs[float]):Poly=
## Initializes a polynomial with given coefficients.
## The most significant coefficient is first, so to create x^2-2x+3:
@ -158,7 +158,7 @@ proc initPoly*(cofs:varargs[float]):Poly=
# reverse order of coefficients so indexing matches degree of
# coefficient...
result.cofs= @[]
for idx in countdown(cofs.len-1,0):
for idx in countdown(cofs.len-1,0):
result.cofs.add(cofs[idx])
result.clean #remove leading zero terms
@ -167,49 +167,49 @@ proc initPoly*(cofs:varargs[float]):Poly=
proc divMod*(p,d:Poly;q,r:var Poly)=
## Divides `p` with `d`, and stores the quotinent in `q` and
## the remainder in `d`
var
var
pdeg=p.degree
ddeg=d.degree
power=p.degree-d.degree
ratio:float
r.cofs = p.cofs #initial remainder=numerator
if power<0: #denominator is larger than numerator
q.cofs= @ [0.0] #quotinent is 0.0
return # keep remainder as numerator
q.cofs=newSeq[float](power+1)
for i in countdown(pdeg,ddeg):
ratio=r.cofs[i]/d.cofs[ddeg]
q.cofs[i-ddeg]=ratio
r.cofs[i]=0.0
for j in countup(0,<ddeg):
var idx=i-ddeg+j
r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
r.clean # drop zero coefficients in remainder
proc `+` *(p1:Poly,p2:Poly):Poly=
## Adds two polynomials
var n=max(p1.cofs.len,p2.cofs.len)
newSeq(result.cofs,n)
for idx in countup(0,n-1):
result[idx]=p1[idx]+p2[idx]
result.clean # drop zero coefficients in remainder
proc `*` *(p1:Poly,p2:Poly):Poly=
## Multiplies the polynomial `p1` with `p2`
var
var
d1=p1.degree
d2=p2.degree
n=d1+d2
idx:int
newSeq(result.cofs,n)
for i1 in countup(0,d1):
@ -225,38 +225,38 @@ proc `*` *(p:Poly,f:float):Poly=
for i in 0..high(p.cofs):
result[i]=p.cofs[i]*f
result.clean
proc `*` *(f:float,p:Poly):Poly=
## Multiplies a real number with a polynomial
return p*f
proc `-`*(p:Poly):Poly=
## Negates a polynomial
result=p
for i in countup(0,<result.cofs.len):
result.cofs[i]= -result.cofs[i]
proc `-` *(p1:Poly,p2:Poly):Poly=
## Subtract `p1` with `p2`
var n=max(p1.cofs.len,p2.cofs.len)
newSeq(result.cofs,n)
for idx in countup(0,n-1):
result[idx]=p1[idx]-p2[idx]
result.clean # drop zero coefficients in remainder
proc `/`*(p:Poly,f:float):Poly=
## Divides polynomial `p` with a real number `f`
newSeq(result.cofs,p.cofs.len)
for i in 0..high(p.cofs):
result[i]=p.cofs[i]/f
result.clean
proc `/` *(p,q:Poly):Poly=
## Divides polynomial `p` with polynomial `q`
var dummy:Poly
p.divMod(q,result,dummy)
p.divMod(q,result,dummy)
proc `mod` *(p,q:Poly):Poly=
## Computes the polynomial modulo operation,
@ -277,20 +277,20 @@ proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
## Solves the quadric equation `ax^2+bx+c`, with a possible
## tolerance `zerotol` to find roots of curves just 'touching'
## the x axis. Returns sequence with 0,1 or 2 solutions.
var p,q,d:float
p=b/(2.0*a)
if p==Inf or p==NegInf: #linear equation..
var linrt= -c/b
if linrt==Inf or linrt==NegInf: #constant only
return @[]
return @[linrt]
q=c/a
d=p*p-q
if d<0.0:
#check for inside zerotol range for neg. roots
var err=a*p*p-b*p+c #evaluate error at parabola center axis
@ -309,12 +309,12 @@ proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
var deg=p.degree
var d=p[deg]
var bound1,bound2:float
for i in countup(0,deg):
var c=abs(p.cofs[i]/d)
bound1=max(bound1,c+1.0)
bound2=bound2+c
bound2=max(1.0,bound2)
result.xmax=min(bound1,bound2)
result.xmin= -result.xmax
@ -327,13 +327,13 @@ proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxite
var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
if br.success:
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
res.add(br.rootx)
res.add(br.rootx)
else:
#this might be a 'touching' case, check function value against
#zero tolerance
if abs(br.rooty)<=zerotol:
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
res.add(br.rootx)
res.add(br.rootx)
proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=

View file

@ -216,7 +216,7 @@ proc hash*[T](x: Rational[T]): Hash =
h = h !& hash(copy.num)
h = h !& hash(copy.den)
result = !$h
when isMainModule:
var
z = Rational[int](num: 0, den: 1)

View file

@ -11,7 +11,7 @@
## redis-server instance, send commands and receive replies.
##
## **Beware**: Most (if not all) functions that return a ``RedisString`` may
## return ``redisNil``, and functions which return a ``RedisList``
## return ``redisNil``, and functions which return a ``RedisList``
## may return ``nil``.
import sockets, os, strutils, parseutils
@ -19,7 +19,7 @@ import sockets, os, strutils, parseutils
const
redisNil* = "\0\0"
type
type
Pipeline = ref object
enabled: bool
buffer: string
@ -34,7 +34,7 @@ type
socket: Socket
connected: bool
pipeline: Pipeline
RedisStatus* = string
RedisInteger* = BiggestInt
RedisString* = string ## Bulk reply
@ -59,10 +59,10 @@ proc open*(host = "localhost", port = 6379.Port): Redis =
if result.socket == invalidSocket:
raiseOSError(osLastError())
result.socket.connect(host, port)
result.pipeline = newPipeline()
result.pipeline = newPipeline()
proc raiseInvalidReply(expected, got: char) =
raise newException(ReplyError,
raise newException(ReplyError,
"Expected '$1' at the beginning of a status reply got '$2'" %
[$expected, $got])
@ -90,16 +90,16 @@ proc parseStatus(r: Redis, line: string = ""): RedisStatus =
raise newException(RedisError, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
return line.substr(1) # Strip '+'
proc readStatus(r:Redis): RedisStatus =
r.readSocket("PIPELINED")
return r.parseStatus(line)
proc parseInteger(r: Redis, line: string = ""): RedisInteger =
if r.pipeline.enabled: return -1
#if line == "+QUEUED": # inside of multi
# return -1
@ -110,10 +110,10 @@ proc parseInteger(r: Redis, line: string = ""): RedisInteger =
raise newException(RedisError, strip(line))
if line[0] != ':':
raiseInvalidReply(':', line[0])
# Strip ':'
if parseBiggestInt(line, result, 1) == 0:
raise newException(ReplyError, "Unable to parse integer.")
raise newException(ReplyError, "Unable to parse integer.")
proc readInteger(r: Redis): RedisInteger =
r.readSocket(-1)
@ -126,19 +126,19 @@ proc recv(sock: Socket, size: int): TaintedString =
proc parseSingleString(r: Redis, line:string, allowMBNil = false): RedisString =
if r.pipeline.enabled: return ""
# Error.
if line[0] == '-':
raise newException(RedisError, strip(line))
# Some commands return a /bulk/ value or a /multi-bulk/ nil. Odd.
if allowMBNil:
if line == "*-1":
return redisNil
if line[0] != '$':
raiseInvalidReply('$', line[0])
var numBytes = parseInt(line.substr(1))
if numBytes == -1:
return redisNil
@ -168,7 +168,7 @@ proc parseArrayLines(r: Redis, countLine:string): RedisList =
proc readArrayLines(r: Redis): RedisList =
r.readSocket(nil)
return r.parseArrayLines(line)
return r.parseArrayLines(line)
proc parseBulkString(r: Redis, allowMBNil = false, line:string = ""): RedisString =
if r.pipeline.enabled: return ""
@ -191,7 +191,7 @@ proc readNext(r: Redis): RedisList =
of ':': @[$(r.parseInteger(line))]
of '$': @[r.parseBulkString(true,line)]
of '*': r.parseArrayLines(line)
else:
else:
raise newException(ReplyError, "readNext failed on line: " & line)
nil
r.pipeline.expected -= 1
@ -202,10 +202,10 @@ proc flushPipeline*(r: Redis, wasMulti = false): RedisList =
if r.pipeline.buffer.len > 0:
r.socket.send(r.pipeline.buffer)
r.pipeline.buffer = ""
r.pipeline.enabled = false
result = @[]
var tot = r.pipeline.expected
for i in 0..tot-1:
@ -232,7 +232,7 @@ proc sendCommand(r: Redis, cmd: string, args: varargs[string]) =
for i in items(args):
request.add("$" & $i.len() & "\c\L")
request.add(i & "\c\L")
if r.pipeline.enabled:
r.pipeline.buffer.add(request)
r.pipeline.expected += 1
@ -249,7 +249,7 @@ proc sendCommand(r: Redis, cmd: string, arg1: string,
for i in items(args):
request.add("$" & $i.len() & "\c\L")
request.add(i & "\c\L")
if r.pipeline.enabled:
r.pipeline.expected += 1
r.pipeline.buffer.add(request)
@ -275,7 +275,7 @@ proc expire*(r: Redis, key: string, seconds: int): bool =
return r.readInteger() == 1
proc expireAt*(r: Redis, key: string, timestamp: int): bool =
## Set the expiration for a key as a UNIX timestamp. Returns `false`
## Set the expiration for a key as a UNIX timestamp. Returns `false`
## if the key could not be found or the timeout could not be set.
r.sendCommand("EXPIREAT", key, $timestamp)
return r.readInteger() == 1
@ -301,7 +301,7 @@ proc scan*(r: Redis, cursor: var BiggestInt, pattern: string): RedisList =
cursor = strutils.parseBiggestInt(reply[0])
return reply[1..high(reply)]
proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList =
proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList =
## Find all keys matching the given pattern and yield it to client in portions
## using cursor as a client query identifier.
r.sendCommand("SCAN", $cursor, ["MATCH", pattern, "COUNT", $count])
@ -315,11 +315,11 @@ proc move*(r: Redis, key: string, db: int): bool =
return r.readInteger() == 1
proc persist*(r: Redis, key: string): bool =
## Remove the expiration from a key.
## Remove the expiration from a key.
## Returns `true` when the timeout was removed.
r.sendCommand("PERSIST", key)
return r.readInteger() == 1
proc randomKey*(r: Redis): RedisString =
## Return a random key from the keyspace
r.sendCommand("RANDOMKEY")
@ -327,11 +327,11 @@ proc randomKey*(r: Redis): RedisString =
proc rename*(r: Redis, key, newkey: string): RedisStatus =
## Rename a key.
##
##
## **WARNING:** Overwrites `newkey` if it exists!
r.sendCommand("RENAME", key, newkey)
raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc renameNX*(r: Redis, key, newkey: string): bool =
## Same as ``rename`` but doesn't continue if `newkey` exists.
## Returns `true` if key was renamed.
@ -342,12 +342,12 @@ proc ttl*(r: Redis, key: string): RedisInteger =
## Get the time to live for a key
r.sendCommand("TTL", key)
return r.readInteger()
proc keyType*(r: Redis, key: string): RedisStatus =
## Determine the type stored at key
r.sendCommand("TYPE", key)
return r.readStatus()
# Strings
@ -360,12 +360,12 @@ proc decr*(r: Redis, key: string): RedisInteger =
## Decrement the integer value of a key by one
r.sendCommand("DECR", key)
return r.readInteger()
proc decrBy*(r: Redis, key: string, decrement: int): RedisInteger =
## Decrement the integer value of a key by the given number
r.sendCommand("DECRBY", key, $decrement)
return r.readInteger()
proc get*(r: Redis, key: string): RedisString =
## Get the value of a key. Returns `redisNil` when `key` doesn't exist.
r.sendCommand("GET", key)
@ -397,7 +397,7 @@ proc incrBy*(r: Redis, key: string, increment: int): RedisInteger =
r.sendCommand("INCRBY", key, $increment)
return r.readInteger()
proc setk*(r: Redis, key, value: string) =
proc setk*(r: Redis, key, value: string) =
## Set the string value of a key.
##
## NOTE: This function had to be renamed due to a clash with the `set` type.
@ -410,18 +410,18 @@ proc setNX*(r: Redis, key, value: string): bool =
r.sendCommand("SETNX", key, value)
return r.readInteger() == 1
proc setBit*(r: Redis, key: string, offset: int,
proc setBit*(r: Redis, key: string, offset: int,
value: string): RedisInteger =
## Sets or clears the bit at offset in the string value stored at key
r.sendCommand("SETBIT", key, $offset, value)
return r.readInteger()
proc setEx*(r: Redis, key: string, seconds: int, value: string): RedisStatus =
## Set the value and expiration of a key
r.sendCommand("SETEX", key, $seconds, value)
raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc setRange*(r: Redis, key: string, offset: int,
proc setRange*(r: Redis, key: string, offset: int,
value: string): RedisInteger =
## Overwrite part of a string at key starting at the specified offset
r.sendCommand("SETRANGE", key, $offset, value)
@ -474,7 +474,7 @@ proc hMGet*(r: Redis, key: string, fields: varargs[string]): RedisList =
r.sendCommand("HMGET", key, fields)
return r.readArray()
proc hMSet*(r: Redis, key: string,
proc hMSet*(r: Redis, key: string,
fieldValues: openArray[tuple[field, value: string]]) =
## Set multiple hash fields to multiple values
var args = @[key]
@ -488,7 +488,7 @@ proc hSet*(r: Redis, key, field, value: string): RedisInteger =
## Set the string value of a hash field
r.sendCommand("HSET", key, field, value)
return r.readInteger()
proc hSetNX*(r: Redis, key, field, value: string): RedisInteger =
## Set the value of a hash field, only if the field does **not** exist
r.sendCommand("HSETNX", key, field, value)
@ -498,11 +498,11 @@ proc hVals*(r: Redis, key: string): RedisList =
## Get all the values in a hash
r.sendCommand("HVALS", key)
return r.readArray()
# Lists
proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
## Remove and get the *first* element in a list, or block until
## Remove and get the *first* element in a list, or block until
## one is available
var args: seq[string] = @[]
for i in items(keys): args.add(i)
@ -511,7 +511,7 @@ proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
return r.readArray()
proc bRPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
## Remove and get the *last* element in a list, or block until one
## Remove and get the *last* element in a list, or block until one
## is available.
var args: seq[string] = @[]
for i in items(keys): args.add(i)
@ -539,7 +539,7 @@ proc lInsert*(r: Redis, key: string, before: bool, pivot, value: string):
var pos = if before: "BEFORE" else: "AFTER"
r.sendCommand("LINSERT", key, pos, pivot, value)
return r.readInteger()
proc lLen*(r: Redis, key: string): RedisInteger =
## Get the length of a list
r.sendCommand("LLEN", key)
@ -553,7 +553,7 @@ proc lPop*(r: Redis, key: string): RedisString =
proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
## Prepend a value to a list. Returns the length of the list after the push.
## The ``create`` param specifies whether a list should be created if it
## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH`
## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH`
## will be used, otherwise `LPUSHX`.
if create:
r.sendCommand("LPUSH", key, value)
@ -562,7 +562,7 @@ proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
return r.readInteger()
proc lRange*(r: Redis, key: string, start, stop: int): RedisList =
## Get a range of elements from a list. Returns `nil` when `key`
## Get a range of elements from a list. Returns `nil` when `key`
## doesn't exist.
r.sendCommand("LRANGE", key, $start, $stop)
return r.readArray()
@ -587,16 +587,16 @@ proc rPop*(r: Redis, key: string): RedisString =
## Remove and get the last element in a list
r.sendCommand("RPOP", key)
return r.readBulkString()
proc rPopLPush*(r: Redis, source, destination: string): RedisString =
## Remove the last element in a list, append it to another list and return it
r.sendCommand("RPOPLPUSH", source, destination)
return r.readBulkString()
proc rPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
## Append a value to a list. Returns the length of the list after the push.
## The ``create`` param specifies whether a list should be created if it
## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH`
## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH`
## will be used, otherwise `RPUSHX`.
if create:
r.sendCommand("RPUSH", key, value)
@ -676,7 +676,7 @@ proc sunion*(r: Redis, keys: varargs[string]): RedisList =
proc sunionstore*(r: Redis, destination: string,
key: varargs[string]): RedisInteger =
## Add multiple sets and store the resulting set in a key
## Add multiple sets and store the resulting set in a key
r.sendCommand("SUNIONSTORE", destination, key)
return r.readInteger()
@ -710,16 +710,16 @@ proc zinterstore*(r: Redis, destination: string, numkeys: string,
## a new key
var args = @[destination, numkeys]
for i in items(keys): args.add(i)
if weights.len != 0:
args.add("WITHSCORE")
for i in items(weights): args.add(i)
if aggregate.len != 0:
args.add("AGGREGATE")
args.add(aggregate)
r.sendCommand("ZINTERSTORE", args)
return r.readInteger()
proc zrange*(r: Redis, key: string, start: string, stop: string,
@ -731,18 +731,18 @@ proc zrange*(r: Redis, key: string, start: string, stop: string,
r.sendCommand("ZRANGE", "WITHSCORES", key, start, stop)
return r.readArray()
proc zrangebyscore*(r: Redis, key: string, min: string, max: string,
proc zrangebyscore*(r: Redis, key: string, min: string, max: string,
withScore: bool = false, limit: bool = false,
limitOffset: int = 0, limitCount: int = 0): RedisList =
## Return a range of members in a sorted set, by score
var args = @[key, min, max]
if withScore: args.add("WITHSCORE")
if limit:
if limit:
args.add("LIMIT")
args.add($limitOffset)
args.add($limitCount)
r.sendCommand("ZRANGEBYSCORE", args)
return r.readArray()
@ -770,26 +770,26 @@ proc zremrangebyscore*(r: Redis, key: string, min: string,
proc zrevrange*(r: Redis, key: string, start: string, stop: string,
withScore: bool): RedisList =
## Return a range of members in a sorted set, by index,
## Return a range of members in a sorted set, by index,
## with scores ordered from high to low
if withScore:
r.sendCommand("ZREVRANGE", "WITHSCORE", key, start, stop)
else: r.sendCommand("ZREVRANGE", key, start, stop)
return r.readArray()
proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string,
proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string,
withScore: bool = false, limit: bool = false,
limitOffset: int = 0, limitCount: int = 0): RedisList =
## Return a range of members in a sorted set, by score, with
## scores ordered from high to low
var args = @[key, min, max]
if withScore: args.add("WITHSCORE")
if limit:
if limit:
args.add("LIMIT")
args.add($limitOffset)
args.add($limitCount)
r.sendCommand("ZREVRANGEBYSCORE", args)
return r.readArray()
@ -807,24 +807,24 @@ proc zscore*(r: Redis, key: string, member: string): RedisString =
proc zunionstore*(r: Redis, destination: string, numkeys: string,
keys: openArray[string], weights: openArray[string] = [],
aggregate: string = ""): RedisInteger =
## Add multiple sorted sets and store the resulting sorted set in a new key
## Add multiple sorted sets and store the resulting sorted set in a new key
var args = @[destination, numkeys]
for i in items(keys): args.add(i)
if weights.len != 0:
args.add("WEIGHTS")
for i in items(weights): args.add(i)
if aggregate.len != 0:
args.add("AGGREGATE")
args.add(aggregate)
r.sendCommand("ZUNIONSTORE", args)
return r.readInteger()
# HyperLogLog
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
## Add variable number of elements into special 'HyperLogLog' set type
r.sendCommand("PFADD", key, elements)
return r.readInteger()
@ -864,7 +864,7 @@ proc subscribe*(r: Redis, channel: openarray[string]): ???? =
return ???
proc unsubscribe*(r: Redis, [channel: openarray[string], : string): ???? =
## Stop listening for messages posted to the given channels
## Stop listening for messages posted to the given channels
r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ])
return ???
@ -879,12 +879,12 @@ proc discardMulti*(r: Redis) =
proc exec*(r: Redis): RedisList =
## Execute all commands issued after MULTI
r.sendCommand("EXEC")
r.sendCommand("EXEC")
r.pipeline.enabled = false
# Will reply with +OK for MULTI/EXEC and +QUEUED for every command
# between, then with the results
return r.flushPipeline(true)
proc multi*(r: Redis) =
## Mark the start of a transaction block
@ -898,7 +898,7 @@ proc unwatch*(r: Redis) =
raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc watch*(r: Redis, key: varargs[string]) =
## Watch the given keys to determine execution of the MULTI/EXEC block
## Watch the given keys to determine execution of the MULTI/EXEC block
r.sendCommand("WATCH", key)
raiseNoOK(r.readStatus(), r.pipeline.enabled)
@ -925,7 +925,7 @@ proc quit*(r: Redis) =
raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc select*(r: Redis, index: int): RedisStatus =
## Change the selected database for the current connection
## Change the selected database for the current connection
r.sendCommand("SELECT", $index)
return r.readStatus()
@ -1016,7 +1016,7 @@ proc slaveof*(r: Redis, host: string, port: string) =
iterator hPairs*(r: Redis, key: string): tuple[key, value: string] =
## Iterator for keys and values in a hash.
var
var
contents = r.hGetAll(key)
k = ""
for i in items(contents):
@ -1031,9 +1031,9 @@ proc someTests(r: Redis, how: SendMode):seq[string] =
if how == pipelined:
r.startPipelining()
elif how == multiple:
elif how == multiple:
r.multi()
r.setk("nim:test", "Testing something.")
r.setk("nim:utf8", "こんにちは")
r.setk("nim:esc", "\\ths ągt\\")
@ -1054,7 +1054,7 @@ proc someTests(r: Redis, how: SendMode):seq[string] =
for i in items(p):
if not isNil(i):
list.add(i)
list.add(i)
list.add(r.debugObject("mylist"))
@ -1079,7 +1079,7 @@ proc assertListsIdentical(listA, listB: seq[string]) =
for item in listA:
assert(item == listB[i])
i = i + 1
when not defined(testing) and isMainModule:
when false:
var r = open()

View file

@ -11,7 +11,7 @@
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
const
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of
proc romanToDecimal*(romanVal: string): int =
@ -28,7 +28,7 @@ proc romanToDecimal*(romanVal: string): int =
of 'C', 'c': val = 100
of 'D', 'd': val = 500
of 'M', 'm': val = 1000
else:
else:
raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
if val >= prevVal:
inc(result, val)

View file

@ -29,7 +29,7 @@ const
var
cacheEnabled = false
type
type
Rope* = ref RopeObj ## empty rope is represented by nil
RopeObj {.acyclic.} = object
left, right: Rope
@ -59,7 +59,7 @@ proc newRope(data: string): Rope =
result.length = len(data)
result.data = data
var
var
cache {.threadvar.}: Rope # the root of the cache tree
N {.threadvar.}: Rope # dummy rope needed for splay algorithm
@ -81,7 +81,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope =
t.left = y.right
y.right = t
t = y
if t.left == nil: break
if t.left == nil: break
r.left = t
r = t
t = t.left
@ -91,7 +91,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope =
t.right = y.left
y.left = t
t = y
if t.right == nil: break
if t.right == nil: break
le.right = t
le = t
t = t.right
@ -109,7 +109,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
if t == nil:
result = newRope(s)
when countCacheMisses: inc(misses)
return
return
var cmp: int
t = splay(s, t, cmp)
if cmp == 0:
@ -197,7 +197,7 @@ proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} =
proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} =
## adds `b` to the rope `a`.
a = a & b
proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
## returns the character at position `i` in the rope `r`. This is quite
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
@ -250,7 +250,7 @@ when false:
proc compiledArg(idx: int): Rope =
new(result)
result.length = -idx
proc compileFrmt(frmt: string): Rope =
var i = 0
var length = len(frmt)
@ -272,7 +272,7 @@ when false:
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
if frmt[i] notin {'0'..'9'}: break
if frmt[i] notin {'0'..'9'}: break
add(s, compiledArg(j))
of '{':
inc(i)
@ -345,7 +345,7 @@ const
proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} =
## returns true if the contents of the file `f` equal `r`.
var
var
buf: array[bufSize, char]
bpos = buf.len
blen = buf.len
@ -363,7 +363,7 @@ proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} =
return
let n = min(blen - bpos, slen - spos)
# TODO There's gotta be a better way of comparing here...
if not equalMem(addr(buf[bpos]),
if not equalMem(addr(buf[bpos]),
cast[pointer](cast[int](cstring(s))+spos), n):
result = false
return

View file

@ -8,13 +8,13 @@
#
## This module implements helper procs for SCGI applications. Example:
##
##
## .. code-block:: Nim
##
## import strtabs, sockets, scgi
##
## var counter = 0
## proc handleRequest(client: Socket, input: string,
## proc handleRequest(client: Socket, input: string,
## headers: StringTableRef): bool {.procvar.} =
## inc(counter)
## client.writeStatusOkTextContent()
@ -37,19 +37,19 @@ import sockets, strutils, os, strtabs, asyncio
type
ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
proc raiseScgiError*(msg: string) {.noreturn.} =
proc raiseScgiError*(msg: string) {.noreturn.} =
## raises an ScgiError exception with message `msg`.
var e: ref ScgiError
new(e)
e.msg = msg
raise e
proc parseWord(inp: string, outp: var string, start: int): int =
proc parseWord(inp: string, outp: var string, start: int): int =
result = start
while inp[result] != '\0': inc(result)
outp = substr(inp, start, result-1)
proc parseHeaders(s: string, L: int): StringTableRef =
proc parseHeaders(s: string, L: int): StringTableRef =
result = newStringTable()
var i = 0
while i < L:
@ -59,12 +59,12 @@ proc parseHeaders(s: string, L: int): StringTableRef =
result[key] = val
if s[i] == ',': inc(i)
else: raiseScgiError("',' after netstring expected")
proc recvChar(s: Socket): char =
proc recvChar(s: Socket): char =
var c: char
if recv(s, addr(c), sizeof(c)) == sizeof(c):
if recv(s, addr(c), sizeof(c)) == sizeof(c):
result = c
type
ScgiState* = object of RootObj ## SCGI state object
server: Socket
@ -72,22 +72,22 @@ type
client*: Socket ## the client socket to send data to
headers*: StringTableRef ## the parsed headers
input*: string ## the input buffer
# Async
ClientMode = enum
ClientReadChar, ClientReadHeaders, ClientReadContent
AsyncClient = ref object
c: AsyncSocket
mode: ClientMode
dataLen: int
headers: StringTableRef ## the parsed headers
input: string ## the input buffer
AsyncScgiStateObj = object
handleRequest: proc (client: AsyncSocket,
handleRequest: proc (client: AsyncSocket,
input: string,
headers: StringTableRef) {.closure, gcsafe.}
asyncServer: AsyncSocket
@ -98,19 +98,19 @@ type
PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
proc recvBuffer(s: var ScgiState, L: int) =
if L > s.bufLen:
if L > s.bufLen:
s.bufLen = L
s.input = newString(L)
if L > 0 and recv(s.client, cstring(s.input), L) != L:
if L > 0 and recv(s.client, cstring(s.input), L) != L:
raiseScgiError("could not read all data")
setLen(s.input, L)
proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
reuseAddr = false) =
reuseAddr = false) =
## opens a connection.
s.bufLen = 4000
s.input = newString(s.bufLen) # will be reused
s.server = socket()
if s.server == invalidSocket: raiseOSError(osLastError())
new(s.client) # Initialise s.client for `next`
@ -120,12 +120,12 @@ proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
s.server.setSockOpt(OptReuseAddr, true)
bindAddr(s.server, port, address)
listen(s.server)
proc close*(s: var ScgiState) =
proc close*(s: var ScgiState) =
## closes the connection.
s.server.close()
proc next*(s: var ScgiState, timeout: int = -1): bool =
proc next*(s: var ScgiState, timeout: int = -1): bool =
## proceed to the first/next request. Waits ``timeout`` milliseconds for a
## request, if ``timeout`` is `-1` then this function will never time out.
## Returns `true` if a new request has been processed.
@ -139,18 +139,18 @@ proc next*(s: var ScgiState, timeout: int = -1): bool =
if d == '\0':
s.client.close()
return false
if d notin strutils.Digits:
if d notin strutils.Digits:
if d != ':': raiseScgiError("':' after length expected")
break
L = L * 10 + ord(d) - ord('0')
L = L * 10 + ord(d) - ord('0')
recvBuffer(s, L+1)
s.headers = parseHeaders(s.input, L)
if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
L = parseInt(s.headers["CONTENT_LENGTH"])
recvBuffer(s, L)
return true
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
## sends the following string to the socket `c`::
##
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
@ -159,9 +159,9 @@ proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
c.send("Status: 200 OK\r\L" &
"Content-Type: $1\r\L\r\L" % contentType)
proc run*(handleRequest: proc (client: Socket, input: string,
proc run*(handleRequest: proc (client: Socket, input: string,
headers: StringTableRef): bool {.nimcall,gcsafe.},
port = Port(4000)) =
port = Port(4000)) =
## encapsulates the SCGI object and main loop.
var s: ScgiState
s.open(port)
@ -197,7 +197,7 @@ proc checkCloseSocket(client: AsyncClient) =
s.close()
s.delHandleWrite()
else: client.c.close()
proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
case client.mode
of ClientReadChar:
@ -223,7 +223,7 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
client.headers = parseHeaders(client.input, client.input.len-1)
if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
client.input = "" # For next part
let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
if contentLen > 0:
client.mode = ClientReadContent
@ -250,12 +250,12 @@ proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
accept(s.asyncServer, client)
var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
headers: newStringTable(), input: "")
client.handleRead =
client.handleRead =
proc (sock: AsyncSocket) =
handleClientRead(asyncClient, s)
s.disp.register(client)
proc open*(handleRequest: proc (client: AsyncSocket,
proc open*(handleRequest: proc (client: AsyncSocket,
input: string, headers: StringTableRef) {.
closure, gcsafe.},
port = Port(4000), address = "127.0.0.1",
@ -286,7 +286,7 @@ proc close*(s: AsyncScgiState) =
when false:
var counter = 0
proc handleRequest(client: Socket, input: string,
proc handleRequest(client: Socket, input: string,
headers: StringTableRef): bool {.procvar.} =
inc(counter)
client.writeStatusOkTextContent()

View file

@ -7,25 +7,25 @@
# distribution, for details about the copyright.
#
## This module implements the SMTP client protocol as specified by RFC 5321,
## This module implements the SMTP client protocol as specified by RFC 5321,
## this can be used to send mail to any SMTP Server.
##
## This module also implements the protocol used to format messages,
##
## This module also implements the protocol used to format messages,
## as specified by RFC 2822.
##
##
## Example gmail use:
##
##
##
##
## .. code-block:: Nim
## var msg = createMessage("Hello from Nim's SMTP",
## "Hello!.\n Is this awesome or what?",
## var msg = createMessage("Hello from Nim's SMTP",
## "Hello!.\n Is this awesome or what?",
## @["foo@gmail.com"])
## var smtp = connect("smtp.gmail.com", 465, true, true)
## smtp.auth("username", "password")
## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
##
##
## For SSL support this module relies on OpenSSL. If you want to
##
##
## For SSL support this module relies on OpenSSL. If you want to
## enable SSL, compile with ``-d:ssl``.
import net, strutils, strtabs, base64, os
@ -35,14 +35,14 @@ type
Smtp* = object
sock: Socket
debug: bool
Message* = object
msgTo: seq[string]
msgCc: seq[string]
msgSubject: string
msgOtherHeaders: StringTableRef
msgBody: string
ReplyError* = object of IOError
AsyncSmtp* = ref object
@ -84,7 +84,7 @@ when not defined(ssl):
else:
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
proc connect*(address: string, port = Port(25),
proc connect*(address: string, port = Port(25),
ssl = false, debug = false,
sslContext = defaultSSLContext): Smtp =
## Establishes a connection with a SMTP server.
@ -94,17 +94,17 @@ proc connect*(address: string, port = Port(25),
when compiledWithSsl:
sslContext.wrapSocket(result.sock)
else:
raise newException(ESystem,
raise newException(ESystem,
"SMTP module compiled without SSL support")
result.sock.connect(address, port)
result.debug = debug
result.checkReply("220")
result.debugSend("HELO " & address & "\c\L")
result.checkReply("250")
proc auth*(smtp: var Smtp, username, password: string) =
## Sends an AUTH command to the server to login as the `username`
## Sends an AUTH command to the server to login as the `username`
## using `password`.
## May fail with ReplyError.
@ -113,13 +113,13 @@ proc auth*(smtp: var Smtp, username, password: string) =
# i.e "334 VXNlcm5hbWU6"
smtp.debugSend(encode(username) & "\c\L")
smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
smtp.debugSend(encode(password) & "\c\L")
smtp.checkReply("235") # Check whether the authentification was successful.
proc sendmail*(smtp: var Smtp, fromaddr: string,
toaddrs: seq[string], msg: string) =
## Sends `msg` from `fromaddr` to `toaddr`.
## Sends `msg` from `fromaddr` to `toaddr`.
## Messages may be formed using ``createMessage`` by converting the
## Message into a string.
@ -128,7 +128,7 @@ proc sendmail*(smtp: var Smtp, fromaddr: string,
for address in items(toaddrs):
smtp.debugSend("RCPT TO:<" & address & ">\c\L")
smtp.checkReply("250")
# Send the message
smtp.debugSend("DATA " & "\c\L")
smtp.checkReply("354")
@ -175,7 +175,7 @@ proc `$`*(msg: Message): string =
result.add("\c\L")
result.add(msg.msgBody)
proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
sslContext = defaultSslContext): AsyncSmtp =
## Creates a new ``AsyncSmtp`` instance.
@ -189,7 +189,7 @@ proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
when compiledWithSsl:
sslContext.wrapSocket(result.sock)
else:
raise newException(ESystem,
raise newException(ESystem,
"SMTP module compiled without SSL support")
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
@ -216,7 +216,7 @@ proc connect*(smtp: AsyncSmtp) {.async.} =
await smtp.checkReply("250")
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
## Sends an AUTH command to the server to login as the `username`
## Sends an AUTH command to the server to login as the `username`
## using `password`.
## May fail with ReplyError.
@ -225,7 +225,7 @@ proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
# i.e "334 VXNlcm5hbWU6"
await smtp.sock.send(encode(username) & "\c\L")
await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
await smtp.sock.send(encode(password) & "\c\L")
await smtp.checkReply("235") # Check whether the authentification was successful.
@ -240,7 +240,7 @@ proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
for address in items(toAddrs):
await smtp.sock.send("RCPT TO:<" & address & ">\c\L")
await smtp.checkReply("250")
# Send the message
await smtp.sock.send("DATA " & "\c\L")
await smtp.checkReply("354")
@ -254,24 +254,24 @@ proc close*(smtp: AsyncSmtp) {.async.} =
smtp.sock.close()
when not defined(testing) and isMainModule:
#var msg = createMessage("Test subject!",
#var msg = createMessage("Test subject!",
# "Hello, my name is dom96.\n What\'s yours?", @["dominik@localhost"])
#echo(msg)
#var smtp = connect("localhost", 25, False, True)
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
#echo(decode("a17sm3701420wbe.12"))
proc main() {.async.} =
var client = newAsyncSmtp("smtp.gmail.com", Port(465), true)
await client.connect()
await client.auth("johndoe", "foo")
var msg = createMessage("Hello from Nim's SMTP!",
"Hello!!!!.\n Is this awesome or what?",
var msg = createMessage("Hello from Nim's SMTP!",
"Hello!!!!.\n Is this awesome or what?",
@["blah@gmail.com"])
echo(msg)
await client.sendMail("blah@gmail.com", @["blah@gmail.com"], $msg)
await client.close()
waitFor main()

View file

@ -51,17 +51,17 @@ else:
# Note: The enumerations are mapped to Window's constants.
when defined(ssl):
when defined(ssl):
type
SSLError* = object of Exception
SSLCVerifyMode* = enum
CVerifyNone, CVerifyPeer
SSLProtVersion* = enum
protSSLv2, protSSLv3, protTLSv1, protSSLv23
SSLContext* = distinct SSLCTX
SSLAcceptResult* = enum
@ -93,11 +93,11 @@ type
sslPeekChar: char
of false: nil
nonblocking: bool
Socket* = ref SocketImpl
Port* = distinct uint16 ## port type
Domain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed
## here are unsupported.
@ -112,7 +112,7 @@ type
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
Protocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
@ -178,7 +178,7 @@ proc `==`*(a, b: Port): bool {.borrow.}
proc `$`*(p: Port): string {.borrow.}
## returns the port number as a string
proc ntohl*(x: int32): int32 =
proc ntohl*(x: int32): int32 =
## Converts 32-bit integers from network to host byte order.
## On machines where the host byte order is the same as network byte order,
## this is a no-op; otherwise, it performs a 4-byte swap operation.
@ -206,7 +206,7 @@ proc htons*(x: int16): int16 =
## On machines where the host byte order is the same as network byte
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = sockets.ntohs(x)
when defined(Posix):
proc toInt(domain: Domain): cint =
case domain
@ -234,19 +234,19 @@ when defined(Posix):
else: discard
else:
proc toInt(domain: Domain): cint =
proc toInt(domain: Domain): cint =
result = toU16(ord(domain))
proc toInt(typ: SockType): cint =
result = cint(ord(typ))
proc toInt(p: Protocol): cint =
result = cint(ord(p))
proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
## Creates a new socket; returns `InvalidSocket` if an error occurs.
# TODO: Perhaps this should just raise EOS when an error occurs.
when defined(Windows):
result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
@ -277,27 +277,27 @@ when defined(ssl):
raise newException(system.IOError, "Certificate file could not be found: " & certFile)
if keyFile != "" and not existsFile(keyFile):
raise newException(system.IOError, "Key file could not be found: " & keyFile)
if certFile != "":
var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
if ret != 1:
raiseSslError()
# TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
if keyFile != "":
if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
SSL_FILETYPE_PEM) != 1:
raiseSslError()
if SSL_CTX_check_private_key(ctx) != 1:
raiseSslError("Verification of private key file failed.")
proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
certFile = "", keyFile = ""): SSLContext =
## Creates an SSL context.
##
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
## are available with the addition of ``ProtSSLv23`` which allows for
##
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
## are available with the addition of ``ProtSSLv23`` which allows for
## compatibility with all of them.
##
## There are currently only two options for verify mode;
@ -322,7 +322,7 @@ when defined(ssl):
newCTX = SSL_CTX_new(SSLv3_method())
of protTLSv1:
newCTX = SSL_CTX_new(TLSv1_method())
if newCTX.SSLCTXSetCipherList("ALL") != 1:
raiseSslError()
case verifyMode
@ -343,7 +343,7 @@ when defined(ssl):
##
## **Disclaimer**: This code is not well tested, may be very unsafe and
## prone to security vulnerabilities.
socket.isSSL = true
socket.sslContext = ctx
socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
@ -351,7 +351,7 @@ when defined(ssl):
socket.sslHasPeekChar = false
if socket.sslHandle == nil:
raiseSslError()
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
raiseSslError()
@ -382,7 +382,7 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
raiseSslError()
else: raiseSslError("Unknown Error")
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
let lastError = osLastError()
if async:
@ -397,15 +397,15 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
else: raiseOSError(lastError)
proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
proc invalidIp4(s: string) {.noreturn, noinline.} =
raise newException(ValueError, "invalid ip4 address: " & s)
proc parseIp4*(s: string): BiggestInt =
proc parseIp4*(s: string): BiggestInt =
## parses an IP version 4 in dotted decimal form like "a.b.c.d".
##
## This is equivalent to `inet_ntoa`:idx:.
@ -469,8 +469,8 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
gaiNim(address, port, hints, aiList)
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
raiseOSError(osLastError())
proc getSockName*(socket: Socket): Port =
proc getSockName*(socket: Socket): Port =
## returns the socket's associated port number.
var name: Sockaddr_in
when defined(Windows):
@ -485,14 +485,14 @@ proc getSockName*(socket: Socket): Port =
raiseOSError(osLastError())
result = Port(sockets.ntohs(name.sin_port))
template acceptAddrPlain(noClientRet, successRet: expr,
template acceptAddrPlain(noClientRet, successRet: expr,
sslImplementation: stmt): stmt {.immediate.} =
assert(client != nil)
var sockAddress: Sockaddr_in
var addrLen = sizeof(sockAddress).SockLen
var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
addr(addrLen))
if sock == osInvalidSocket:
let err = osLastError()
when defined(windows):
@ -537,7 +537,7 @@ 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
## **Note**: ``client`` must be initialised (with ``new``), this function
## makes no effort to initialise the ``client`` variable.
##
## **Warning:** When using SSL with non-blocking sockets, it is best to use
@ -546,7 +546,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
when defined(ssl):
if server.isSSL:
# We must wrap the client sock in a ssl context.
server.sslContext.wrapSocket(client)
let ret = SSLAccept(client.sslHandle)
while ret <= 0:
@ -572,9 +572,9 @@ when defined(ssl):
proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): SSLAcceptResult {.
tags: [ReadIOEffect].} =
## This procedure should only be used for non-blocking **SSL** sockets.
## This procedure should only be used for non-blocking **SSL** sockets.
## It will immediately return with one of the following values:
##
##
## ``AcceptSuccess`` will be returned when a client has been successfully
## accepted and the handshake has been successfully performed between
## ``server`` and the newly connected client.
@ -591,7 +591,7 @@ when defined(ssl):
if server.isSSL:
client.setBlocking(false)
# We must wrap the client sock in a ssl context.
if not client.isSSL or client.sslHandle == nil:
server.sslContext.wrapSocket(client)
let ret = SSLAccept(client.sslHandle)
@ -623,10 +623,10 @@ when defined(ssl):
proc accept*(server: Socket, client: var Socket) {.tags: [ReadIOEffect].} =
## 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.
var addrDummy = ""
acceptAddr(server, client, addrDummy)
@ -662,7 +662,7 @@ proc close*(socket: Socket) =
socket.sslHandle = nil
proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for
## Searches the database from the beginning and finds the first entry for
## which the service name specified by ``name`` matches the s_name member
## and the protocol name specified by ``proto`` matches the s_proto member.
##
@ -676,10 +676,10 @@ proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = Port(s.s_port)
result.proto = $s.s_proto
proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for
## which the port specified by ``port`` matches the s_port member and the
proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for
## which the port specified by ``port`` matches the s_port member and the
## protocol name specified by ``proto`` matches the s_proto member.
##
## On posix this will search through the ``/etc/services`` file.
@ -697,20 +697,20 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
## This function will lookup the hostname of an IP Address.
var myaddr: InAddr
myaddr.s_addr = inet_addr(ip)
when defined(windows):
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(sockets.AF_INET))
if s == nil: raiseOSError(osLastError())
else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
cint(posix.AF_INET))
if s == nil:
raiseOSError(osLastError(), $hstrerror(h_errno))
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows):
when defined(windows):
result.addrtype = Domain(s.h_addrtype)
else:
if s.h_addrtype == posix.AF_INET:
@ -722,7 +722,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
## This function will lookup the IP address of a hostname.
when defined(Windows):
var s = winlean.gethostbyname(name)
@ -731,7 +731,7 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
if s == nil: raiseOSError(osLastError())
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows):
when defined(windows):
result.addrtype = Domain(s.h_addrtype)
else:
if s.h_addrtype == posix.AF_INET:
@ -744,11 +744,11 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
result.length = int(s.h_length)
proc getSockOptInt*(socket: Socket, level, optname: int): int {.
tags: [ReadIOEffect].} =
tags: [ReadIOEffect].} =
## getsockopt for integer options.
var res: cint
var size = sizeof(res).SockLen
if getsockopt(socket.fd, cint(level), cint(optname),
if getsockopt(socket.fd, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32:
raiseOSError(osLastError())
result = int(res)
@ -757,7 +757,7 @@ proc setSockOptInt*(socket: Socket, level, optname, optval: int) {.
tags: [WriteIOEffect].} =
## setsockopt for integer options.
var value = cint(optval)
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
sizeof(value).SockLen) < 0'i32:
raiseOSError(osLastError())
@ -776,7 +776,7 @@ proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
## Retrieves option ``opt`` as a boolean value.
var res: cint
var size = sizeof(res).SockLen
if getsockopt(socket.fd, cint(level), toCInt(opt),
if getsockopt(socket.fd, cint(level), toCInt(opt),
addr(res), addr(size)) < 0'i32:
raiseOSError(osLastError())
result = res != 0
@ -785,11 +785,11 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
tags: [WriteIOEffect].} =
## Sets option ``opt`` to a boolean value specified by ``value``.
var valuei = cint(if value: 1 else: 0)
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
sizeof(valuei).SockLen) < 0'i32:
raiseOSError(osLastError())
proc connect*(socket: Socket, address: string, port = Port(0),
proc connect*(socket: Socket, address: string, port = Port(0),
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
## host name. If ``address`` is a host name, this function will try each IP
@ -816,7 +816,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
freeaddrinfo(aiList)
if not success: raiseOSError(lastError)
when defined(ssl):
if socket.isSSL:
let ret = SSLConnect(socket.sslHandle)
@ -825,7 +825,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
case err
of SSL_ERROR_ZERO_RETURN:
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
SSL_ERROR_WANT_ACCEPT:
raiseSslError("The operation did not complete. Perhaps you should use connectAsync?")
of SSL_ERROR_WANT_X509_LOOKUP:
@ -834,7 +834,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
raiseSslError()
else:
raiseSslError("Unknown error")
when false:
var s: TSockAddrIn
s.sin_addr.s_addr = inet_addr(address)
@ -842,7 +842,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
when defined(windows):
s.sin_family = toU16(ord(af))
else:
case af
case af
of AF_UNIX: s.sin_family = posix.AF_UNIX
of AF_INET: s.sin_family = posix.AF_INET
of AF_INET6: s.sin_family = posix.AF_INET6
@ -886,7 +886,7 @@ proc connectAsync*(socket: Socket, name: string, port = Port(0),
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
success = true
break
it = it.ai_next
freeaddrinfo(aiList)
@ -942,12 +942,12 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
FD_ZERO(fd)
for i in items(s):
for i in items(s):
m = max(m, int(i.fd))
FD_SET(i.fd, fd)
proc pruneSocketSet(s: var seq[Socket], fd: var TFdSet) =
var i = 0
var L = s.len
@ -982,13 +982,13 @@ proc checkBuffer(readfds: var seq[Socket]): int =
if result > 0:
readfds = res
proc select*(readfds, writefds, exceptfds: var seq[Socket],
timeout = 500): int {.tags: [ReadIOEffect].} =
proc select*(readfds, writefds, exceptfds: var seq[Socket],
timeout = 500): int {.tags: [ReadIOEffect].} =
## Traditional select function. This function will return the number of
## sockets that are ready to be read from, written to, or which have errors.
## If there are none; 0 is returned.
## If there are none; 0 is returned.
## ``Timeout`` is in milliseconds and -1 can be specified for no timeout.
##
##
## Sockets which are **not** ready for reading, writing or which don't have
## errors waiting on them are removed from the ``readfds``, ``writefds``,
## ``exceptfds`` sequences respectively.
@ -997,44 +997,44 @@ proc select*(readfds, writefds, exceptfds: var seq[Socket],
return buffersFilled
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var rd, wr, ex: TFdSet
var m = 0
createFdSet((rd), readfds, m)
createFdSet((wr), writefds, m)
createFdSet((ex), exceptfds, m)
if timeout != -1:
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
pruneSocketSet(readfds, (rd))
pruneSocketSet(writefds, (wr))
pruneSocketSet(exceptfds, (ex))
proc select*(readfds, writefds: var seq[Socket],
proc select*(readfds, writefds: var seq[Socket],
timeout = 500): int {.tags: [ReadIOEffect].} =
## Variant of select with only a read and write list.
let buffersFilled = checkBuffer(readfds)
if buffersFilled > 0:
return buffersFilled
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet
var m = 0
createFdSet((rd), readfds, m)
createFdSet((wr), writefds, m)
if timeout != -1:
result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
pruneSocketSet(readfds, (rd))
pruneSocketSet(writefds, (wr))
proc selectWrite*(writefds: var seq[Socket],
proc selectWrite*(writefds: var seq[Socket],
timeout = 500): int {.tags: [ReadIOEffect].} =
## When a socket in ``writefds`` is ready to be written to then a non-zero
## value will be returned specifying the count of the sockets which can be
@ -1044,16 +1044,16 @@ proc selectWrite*(writefds: var seq[Socket],
## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
## an unlimited time.
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var wr: TFdSet
var m = 0
createFdSet((wr), writefds, m)
if timeout != -1:
result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
else:
result = int(select(cint(m+1), nil, addr(wr), nil, nil))
pruneSocketSet(writefds, (wr))
proc select*(readfds: var seq[Socket], timeout = 500): int =
@ -1062,16 +1062,16 @@ proc select*(readfds: var seq[Socket], timeout = 500): int =
if buffersFilled > 0:
return buffersFilled
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var rd: TFdSet
var m = 0
createFdSet((rd), readfds, m)
if timeout != -1:
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
pruneSocketSet(readfds, (rd))
proc readIntoBuf(socket: Socket, flags: int32): int =
@ -1107,12 +1107,12 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
if socket.isBuffered:
if socket.bufLen == 0:
retRead(0'i32, 0)
var read = 0
while read < size:
if socket.currPos >= socket.bufLen:
retRead(0'i32, read)
let chunk = min(socket.bufLen-socket.currPos, size-read)
var d = cast[cstring](data)
copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
@ -1155,7 +1155,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
else:
if timeout - int(waited * 1000.0) < 1:
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
when defined(ssl):
if socket.isSSL:
if socket.hasDataBuffered:
@ -1164,7 +1164,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
let sslPending = SSLPending(socket.sslHandle)
if sslPending != 0:
return sslPending
var s = @[socket]
var startTime = epochTime()
let selRet = select(s, timeout - int(waited * 1000.0))
@ -1176,8 +1176,8 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
tags: [ReadIOEffect, TimeEffect].} =
## overload with a ``timeout`` parameter in milliseconds.
var waited = 0.0 # number of seconds already waited
var waited = 0.0 # number of seconds already waited
var read = 0
while read < size:
let avail = waitFor(socket, waited, timeout, size-read, "recv")
@ -1187,7 +1187,7 @@ proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
if result < 0:
return result
inc(read, result)
result = read
proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int =
@ -1231,7 +1231,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
var res = socket.readIntoBuf(0'i32)
if res <= 0:
result = res
c = socket.buffer[socket.currPos]
else:
when defined(ssl):
@ -1239,7 +1239,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
if not socket.sslHasPeekChar:
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
socket.sslHasPeekChar = true
c = socket.sslPeekChar
return
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
@ -1251,11 +1251,11 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
## If a full line is received ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is received then ``line``
## will be set to it.
##
##
## ``True`` is returned if data is available. ``False`` suggests an
## error, EOS exceptions are not raised and ``False`` is simply returned
## instead.
##
##
## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
## will be returned.
##
@ -1264,7 +1264,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
##
## **Deprecated since version 0.9.2**: This function has been deprecated in
## favour of readLine.
template addNLIfEmpty(): stmt =
if line.len == 0:
line.add("\c\L")
@ -1286,7 +1286,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
elif n <= 0: return false
addNLIfEmpty()
return true
elif c == '\L':
elif c == '\L':
addNLIfEmpty()
return true
add(line.string, c)
@ -1298,14 +1298,14 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
## If a full line is read ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line``
## will be set to it.
##
##
## If the socket is disconnected, ``line`` will be set to ``""``.
##
## An EOS exception will be raised in the case of a socket error.
##
## A timeout can be specified in milliseconds, if data is not received within
## the specified time an ETimeout exception will be raised.
template addNLIfEmpty(): stmt =
if line.len == 0:
line.add("\c\L")
@ -1327,12 +1327,12 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
elif n <= 0: socket.raiseSocketError()
addNLIfEmpty()
return
elif c == '\L':
elif c == '\L':
addNLIfEmpty()
return
add(line.string, c)
proc recvLineAsync*(socket: Socket,
proc recvLineAsync*(socket: Socket,
line: var TaintedString): RecvLineResult {.tags: [ReadIOEffect], deprecated.} =
## Similar to ``recvLine`` but designed for non-blocking sockets.
##
@ -1350,21 +1350,21 @@ proc recvLineAsync*(socket: Socket,
while true:
var c: char
var n = recv(socket, addr(c), 1)
if n < 0:
if n < 0:
return (if line.len == 0: RecvFail else: RecvPartialLine)
elif n == 0:
elif n == 0:
return (if line.len == 0: RecvDisconnected else: RecvPartialLine)
if c == '\r':
n = peekChar(socket, c)
if n > 0 and c == '\L':
discard recv(socket, addr(c), 1)
elif n <= 0:
elif n <= 0:
return (if line.len == 0: RecvFail else: RecvPartialLine)
return RecvFullLine
elif c == '\L': return RecvFullLine
add(line.string, c)
proc readLineAsync*(socket: Socket,
proc readLineAsync*(socket: Socket,
line: var TaintedString): ReadLineResult {.tags: [ReadIOEffect].} =
## Similar to ``recvLine`` but designed for non-blocking sockets.
##
@ -1376,24 +1376,24 @@ proc readLineAsync*(socket: Socket,
## * If no data could be retrieved; ``ReadNone`` is returned.
## * If call to ``recv`` failed; **an EOS exception is raised.**
setLen(line.string, 0)
template errorOrNone =
socket.raiseSocketError(async = true)
return ReadNone
while true:
var c: char
var n = recv(socket, addr(c), 1)
#echo(n)
if n < 0:
if line.len == 0: errorOrNone else: return ReadPartialLine
elif n == 0:
elif n == 0:
return (if line.len == 0: ReadDisconnected else: ReadPartialLine)
if c == '\r':
n = peekChar(socket, c)
if n > 0 and c == '\L':
discard recv(socket, addr(c), 1)
elif n <= 0:
elif n <= 0:
if line.len == 0: errorOrNone else: return ReadPartialLine
return ReadFullLine
elif c == '\L': return ReadFullLine
@ -1424,7 +1424,7 @@ proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} =
var bytesRead = recv(socket, cstring(buf), bufSize-1)
# Error
if bytesRead == -1: OSError(osLastError())
buf[bytesRead] = '\0' # might not be necessary
setLen(buf, bytesRead)
add(result.string, buf)
@ -1442,13 +1442,13 @@ proc recvTimeout*(socket: Socket, timeout: int): TaintedString {.
var s = @[socket]
if s.select(timeout) != 1:
raise newException(TimeoutError, "Call to recv() timed out.")
return socket.recv
{.pop.}
proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
tags: [ReadIOEffect], deprecated.} =
## receives all the data from a non-blocking socket. If socket is non-blocking
## receives all the data from a non-blocking socket. If socket is non-blocking
## and there are no messages available, `False` will be returned.
## Other socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected
@ -1478,7 +1478,7 @@ proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
raiseSslError()
else: raiseSslError("Unknown Error")
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
let err = osLastError()
when defined(windows):
@ -1510,7 +1510,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
## so when ``socket`` is buffered the non-buffered implementation will be
## used. Therefore if ``socket`` contains something in its buffer this
## function will make no effort to return it.
# TODO: Buffered sockets
data.setLen(length)
var sockAddress: Sockaddr_in
@ -1524,7 +1524,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
port = ntohs(sockAddress.sin_port).Port
proc recvFromAsync*(socket: Socket, data: var string, length: int,
address: var string, port: var Port,
address: var string, port: var Port,
flags = 0'i32): bool {.tags: [ReadIOEffect].} =
## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``,
## this function will raise an EOS error whenever a socket error occurs.
@ -1573,11 +1573,11 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
when defined(ssl):
if socket.isSSL:
return SSLWrite(socket.sslHandle, cast[cstring](data), size)
when defined(windows) or defined(macosx):
result = send(socket.fd, data, size.cint, 0'i32)
else:
when defined(solaris):
when defined(solaris):
const MSG_NOSIGNAL = 0
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
@ -1590,7 +1590,7 @@ proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} =
when defined(ssl):
if socket.isSSL:
raiseSslError()
raiseOSError(osLastError())
if sent != data.len:
@ -1633,7 +1633,7 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return 0
else: raiseOSError(err)
proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
## safe alternative to ``send``. Does not raise an EOS when an error occurs,
@ -1644,7 +1644,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
size: int, af: Domain = AF_INET, flags = 0'i32): int {.
tags: [WriteIOEffect].} =
## low-level sendTo proc. This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified
## which may be an IP address or a hostname, if a hostname is specified
## this function will try each IP of that hostname.
##
## **Note:** This proc is not available for SSL sockets.
@ -1654,7 +1654,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
hints.ai_socktype = toInt(SOCK_STREAM)
hints.ai_protocol = toInt(IPPROTO_TCP)
gaiNim(address, port, hints, aiList)
# try all possibilities:
var success = false
var it = aiList
@ -1668,7 +1668,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
freeaddrinfo(aiList)
proc sendTo*(socket: Socket, address: string, port: Port,
proc sendTo*(socket: Socket, address: string, port: Port,
data: string): int {.tags: [WriteIOEffect].} =
## Friendlier version of the low-level ``sendTo``.
result = socket.sendTo(address, port, cstring(data), data.len)
@ -1677,10 +1677,10 @@ when defined(Windows):
const
IOCPARM_MASK = 127
IOC_IN = int(-2147483648)
FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
(102 shl 8) or 126
proc ioctlsocket(s: SocketHandle, cmd: clong,
proc ioctlsocket(s: SocketHandle, cmd: clong,
argptr: ptr clong): cint {.
stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
@ -1713,7 +1713,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
## the connection to the server to be made.
let originalStatus = not socket.nonblocking
socket.setBlocking(false)
socket.connectAsync(address, port, af)
var s: seq[Socket] = @[socket]
if selectWrite(s, timeout) != 1:

View file

@ -77,7 +77,7 @@ proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
result = if not negative: j-1 else: a.len-j
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var name = ""
while f[i] in PatternChars:
while f[i] in PatternChars:
name.add(f[i])
inc(i)
result = findNormalized(name, a)+1
@ -131,7 +131,7 @@ proc scanBranch(p: var FormatParser, a: openArray[string],
while true:
case f[i]
of ']': break
of '|':
of '|':
inc i
elsePart = i
inc c
@ -172,7 +172,7 @@ proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
var slice = false
var i = p.i
var f = p.f
if f[i] == '{': inc i
else: raiseInvalidFormat("'{' expected")
if f[i] == '.' and f[i+1] == '.':
@ -193,12 +193,12 @@ proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
if f[i] != '}': raiseInvalidFormat("'}' expected")
inc i
p.i = i
proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
var i = p.i
var f = p.f
case f[i]
of '$':
of '$':
emitChar p, s, '$'
inc i
of '*':
@ -232,7 +232,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
# $' '~{1..3}
# insert space followed by 1..3 if not empty
inc i
call:
call:
let (x, y) = scanSlice(p, a)
var L = 0
for j in x..y: inc L, a[j].len
@ -258,7 +258,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
of 'i':
inc i
callNoLineLenTracking: scanQuote(p, indent, true)
call:
let (x, y) = scanSlice(p, a)
if maxLen < 1: emitStrLinear(p, s, indent)
@ -266,7 +266,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
emitStr p, s, a[x]
for j in x+1..y:
emitStr p, s, sep
if items >= maxLen:
if items >= maxLen:
emitStrLinear p, s, indent
items = 0
emitStr p, s, a[j]
@ -274,7 +274,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
of 'c':
inc i
callNoLineLenTracking: scanQuote(p, indent, true)
call:
let (x, y) = scanSlice(p, a)
if p.lineLen + a[x].len > maxLen: emitStrLinear(p, s, indent)
@ -283,7 +283,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
emitStr p, s, sep
if p.lineLen + a[j].len > maxLen: emitStrLinear(p, s, indent)
emitStr p, s, a[j]
else: raiseInvalidFormat("unit 'c' (chars) or 'i' (items) expected")
break StringJoin
@ -294,7 +294,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
emitStr p, s, sep
emitStr p, s, a[j]
else:
call:
call:
var x = getFormatArg(p, a)
emitStr p, s, a[x]
p.i = i
@ -375,38 +375,38 @@ when isMainModule:
doAssert "$1($', '{2..})" % ["f", "a", "b"] == "f(a, b)"
doAssert "$[$1($', '{2..})|''''|fg'$3']1" % ["7", "a", "b"] == "fg$3"
doAssert "$[$#($', '{#..})|''''|$3]1" % ["0", "a", "b"] == "0(a, b)"
doAssert "$' '~{..}" % "" == ""
doAssert "$' '~{..}" % "P0" == " P0"
doAssert "${$1}" % "1" == "1"
doAssert "${$$-1} $$1" % "1" == "1 $1"
doAssert "$#($', '10c'\n '{#..})" % ["doAssert", "longishA", "longish"] ==
"""doAssert(
longishA,
longishA,
longish)"""
assert "type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] ==
strutils.unindent """
type MyEnum* = enum
fieldA, fieldB,
FiledClkad, fieldD,
fieldA, fieldB,
FiledClkad, fieldD,
fieldE, longishFieldName"""
doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
doAssert subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
assert subex("type\n Enum = enum\n $', '40c'\n '{..}") % [
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] ==
strutils.unindent """
type
Enum = enum
fieldNameA, fieldNameB, fieldNameC,
fieldNameA, fieldNameB, fieldNameC,
fieldNameD"""

View file

@ -1285,7 +1285,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
result = a.len - b.len
proc reversed*(s: string): string =
## Returns the reverse of ``s``, interpreting it as Unicode characters.
## Returns the reverse of ``s``, interpreting it as Unicode characters.
## Unicode combining characters are correctly interpreted as well:
##
## .. code-block:: nim

View file

@ -7,19 +7,19 @@
# distribution, for details about the copyright.
#
## This module is based on Python's Unidecode module by Tomaz Solc,
## which in turn is based on the ``Text::Unidecode`` Perl module by
## Sean M. Burke
## This module is based on Python's Unidecode module by Tomaz Solc,
## which in turn is based on the ``Text::Unidecode`` Perl module by
## Sean M. Burke
## (http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm ).
##
## It provides a single proc that does Unicode to ASCII transliterations:
## It finds the sequence of ASCII characters that is the closest approximation
## to the Unicode string.
##
## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some
## information is lost in this transformation, of course, since several Unicode
## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some
## information is lost in this transformation, of course, since several Unicode
## strings can be transformed in the same ASCII representation. So this is a
## strictly one-way transformation. However a human reader will probably
## strictly one-way transformation. However a human reader will probably
## still be able to guess what original string was meant from the context.
##
## This module needs the data file "unidecode.dat" to work: You can either
@ -31,7 +31,7 @@ import unicode
when defined(embedUnidecodeTable):
import strutils
const translationTable = splitLines(slurp"unidecode/unidecode.dat")
else:
# shared is fine for threading:
@ -49,12 +49,12 @@ proc loadUnidecodeTable*(datafile = "unidecode.dat") =
translationTable[i] = line.string
inc(i)
proc unidecode*(s: string): string =
proc unidecode*(s: string): string =
## Finds the sequence of ASCII characters that is the closest approximation
## to the UTF-8 string `s`.
##
## Example:
##
## Example:
##
## ..code-block:: nim
##
## unidecode("\x53\x17\x4E\xB0")
@ -63,7 +63,7 @@ proc unidecode*(s: string): string =
##
assert(not isNil(translationTable))
result = ""
for r in runes(s):
for r in runes(s):
var c = int(r)
if c <=% 127: add(result, chr(c))
elif c <% translationTable.len: add(result, translationTable[c-128])

View file

@ -30,19 +30,19 @@ proc getNS(prefix: string): string =
if ":" in key:
if key.split(':')[1] == prefix:
return value
if key == "xmlns":
defaultNS.add(value)
# Don't return the default namespaces
# in the loop, because then they would have a precedence
# over normal namespaces
if defaultNS.len() > 0:
return defaultNS[0] # Return the first found default namespace
# if none are specified for this prefix
return ""
proc parseText(x: var XmlParser, doc: var PDocument): PText =
result = doc.createTextNode(x.charData())
@ -58,19 +58,19 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
n.appendChild(parseElement(x, doc))
else:
n = doc.createElementNS("", x.elementName)
of xmlElementEnd:
if x.elementName == n.nodeName:
# n.normalize() # Remove any whitespace etc.
var ns: string
if x.elementName.contains(':'):
ns = getNS(x.elementName.split(':')[0])
else:
ns = getNS("")
n.namespaceURI = ns
# Remove any namespaces this element declared
var count = 0 # Variable which keeps the index
# We need to edit it..
@ -82,15 +82,15 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
return n
else: #The wrong element is ended
raise newException(EMismatchedTag, "Mismatched tag at line " &
raise newException(EMismatchedTag, "Mismatched tag at line " &
$x.getLine() & " column " & $x.getColumn)
of xmlCharData:
n.appendChild(parseText(x, doc))
of xmlAttribute:
if x.attrKey == "xmlns" or x.attrKey.startsWith("xmlns:"):
nsList.add((x.attrKey, x.attrValue, n))
if x.attrKey.contains(':'):
var ns = getNS(x.attrKey)
n.setAttributeNS(ns, x.attrKey, x.attrValue)
@ -103,7 +103,7 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
n.appendChild(doc.createComment(x.charData()))
of xmlPI:
n.appendChild(doc.createProcessingInstruction(x.piName(), x.piRest()))
of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial:
discard " Unused \'events\'"
@ -111,19 +111,19 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
raise newException(EParserError, "Unexpected XML Parser event")
x.next()
raise newException(EMismatchedTag,
raise newException(EMismatchedTag,
"Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn)
proc loadXMLStream*(stream: Stream): PDocument =
## Loads and parses XML from a stream specified by ``stream``, and returns
## Loads and parses XML from a stream specified by ``stream``, and returns
## a ``PDocument``
var x: XmlParser
open(x, stream, nil, {reportComments})
var xmlDoc: PDocument
var dom: PDOMImplementation = getDOM()
while true:
x.next()
case x.kind()
@ -140,16 +140,16 @@ proc loadXMLStream*(stream: Stream): PDocument =
return xmlDoc
proc loadXML*(xml: string): PDocument =
## Loads and parses XML from a string specified by ``xml``, and returns
## Loads and parses XML from a string specified by ``xml``, and returns
## a ``PDocument``
var s = newStringStream(xml)
return loadXMLStream(s)
proc loadXMLFile*(path: string): PDocument =
## Loads and parses XML from a file specified by ``path``, and returns
## Loads and parses XML from a file specified by ``path``, and returns
## a ``PDocument``
var s = newFileStream(path, fmRead)
if s == nil: raise newException(IOError, "Unable to read file " & path)
return loadXMLStream(s)
@ -164,5 +164,5 @@ when not defined(testing) and isMainModule:
if i.namespaceURI != nil:
echo(i.nodeName, "=", i.namespaceURI)
echo($xml)

View file

@ -18,24 +18,24 @@ type
{.deprecated: [EInvalidXml: XmlError].}
proc raiseInvalidXml(errors: seq[string]) =
proc raiseInvalidXml(errors: seq[string]) =
var e: ref XmlError
new(e)
e.msg = errors[0]
e.errors = errors
raise e
proc addNode(father, son: XmlNode) =
proc addNode(father, son: XmlNode) =
if son != nil: add(father, son)
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
proc untilElementEnd(x: var XmlParser, result: XmlNode,
proc untilElementEnd(x: var XmlParser, result: XmlNode,
errors: var seq[string]) =
while true:
case x.kind
of xmlElementEnd:
if x.elementName == result.tag:
of xmlElementEnd:
if x.elementName == result.tag:
next(x)
else:
errors.add(errorMsg(x, "</" & result.tag & "> expected"))
@ -49,7 +49,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
case x.kind
of xmlComment:
of xmlComment:
result = newComment(x.charData)
next(x)
of xmlCharData, xmlWhitespace:
@ -67,11 +67,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
untilElementEnd(x, result, errors)
of xmlElementEnd:
errors.add(errorMsg(x, "unexpected ending tag: " & x.elementName))
of xmlElementOpen:
of xmlElementOpen:
result = newElement(x.elementName)
next(x)
result.attrs = newStringTable()
while true:
while true:
case x.kind
of xmlAttribute:
result.attrs[x.attrKey] = x.attrValue
@ -91,7 +91,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
of xmlAttribute, xmlElementClose:
errors.add(errorMsg(x, "<some_tag> expected"))
next(x)
of xmlCData:
of xmlCData:
result = newCData(x.charData)
next(x)
of xmlEntity:
@ -100,8 +100,8 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
next(x)
of xmlEof: discard
proc parseXml*(s: Stream, filename: string,
errors: var seq[string]): XmlNode =
proc parseXml*(s: Stream, filename: string,
errors: var seq[string]): XmlNode =
## parses the XML from stream `s` and returns a ``PXmlNode``. Every
## occurred parsing error is added to the `errors` sequence.
var x: XmlParser
@ -109,7 +109,7 @@ proc parseXml*(s: Stream, filename: string,
while true:
x.next()
case x.kind
of xmlElementOpen, xmlElementStart:
of xmlElementOpen, xmlElementStart:
result = parse(x, errors)
break
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
@ -120,7 +120,7 @@ proc parseXml*(s: Stream, filename: string,
break
close(x)
proc parseXml*(s: Stream): XmlNode =
proc parseXml*(s: Stream): XmlNode =
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
## errors are turned into an ``EInvalidXML`` exception.
var errors: seq[string] = @[]
@ -128,7 +128,7 @@ proc parseXml*(s: Stream): XmlNode =
if errors.len > 0: raiseInvalidXml(errors)
proc loadXml*(path: string, errors: var seq[string]): XmlNode =
## Loads and parses XML from file specified by ``path``, and returns
## Loads and parses XML from file specified by ``path``, and returns
## a ``PXmlNode``. Every occurred parsing error is added to the `errors`
## sequence.
var s = newFileStream(path, fmRead)
@ -136,9 +136,9 @@ proc loadXml*(path: string, errors: var seq[string]): XmlNode =
result = parseXml(s, path, errors)
proc loadXml*(path: string): XmlNode =
## Loads and parses XML from file specified by ``path``, and returns
## Loads and parses XML from file specified by ``path``, and returns
## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML``
## exception.
## exception.
var errors: seq[string] = @[]
result = loadXml(path, errors)
if errors.len > 0: raiseInvalidXml(errors)
@ -146,14 +146,14 @@ proc loadXml*(path: string): XmlNode =
when not defined(testing) and isMainModule:
import os
var errors: seq[string] = @[]
var errors: seq[string] = @[]
var x = loadXml(paramStr(1), errors)
for e in items(errors): echo e
var f: File
if open(f, "xmltest.txt", fmWrite):
f.write($x)
f.close()
else:
quit("cannot write test.txt")