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
parent d681812465
commit 43bddf62dd
67 changed files with 2435 additions and 2435 deletions

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## A higher level `mySQL`:idx: database wrapper. The same interface is
## A higher level `mySQL`:idx: database wrapper. The same interface is
## implemented for other databases too.
import strutils, mysql
@@ -29,22 +29,22 @@ type
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} =
proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
new(e)
e.msg = $mysql.error(db)
raise e
proc dbError*(msg: string) {.noreturn.} =
proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`.
var e: ref EDb
new(e)
@@ -55,9 +55,9 @@ when false:
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
var stmt = mysql_stmt_init(db)
if stmt == nil: dbError(db)
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
dbError(db)
var
var
binding: seq[MYSQL_BIND]
discard mysql_stmt_close(stmt)
@@ -79,9 +79,9 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
else:
add(result, dbQuote(args[a]))
inc(a)
else:
else:
add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [FReadDB, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise.
@@ -97,19 +97,19 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
## executes the query and raises EDB if not successful.
var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
proc newRow(L: int): Row =
proc newRow(L: int): Row =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
if row != nil:
while mysql.fetchRow(sqlres) != nil: discard
mysql.freeResult(sqlres)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the query and iterates over the result dataset. This is very
## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For MySQL this is the case!.
rawExec(db, query, args)
@@ -121,7 +121,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
while true:
row = mysql.fetchRow(sqlres)
if row == nil: break
for i in 0..L-1:
for i in 0..L-1:
setLen(result[i], 0)
if row[i] == nil:
result[i] = nil
@@ -164,8 +164,8 @@ proc getRow*(db: DbConn, query: SqlQuery,
var L = int(mysql.numFields(sqlres))
result = newRow(L)
var row = mysql.fetchRow(sqlres)
if row != nil:
for i in 0..L-1:
if row != nil:
for i in 0..L-1:
setLen(result[i], 0)
if row[i] == nil:
result[i] = nil
@@ -173,7 +173,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
add(result[i], row[i])
properFreeResult(sqlres, row)
proc getAllRows*(db: DbConn, query: SqlQuery,
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
## executes the query and returns the whole result dataset.
result = @[]
@@ -196,44 +196,44 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
inc(j)
mysql.freeResult(sqlres)
iterator rows*(db: DbConn, query: SqlQuery,
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
result = getRow(db, query, args)[0]
proc tryInsertId*(db: DbConn, query: SqlQuery,
proc tryInsertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32:
if mysql.realQuery(db, q, q.len) != 0'i32:
result = -1'i64
else:
result = mysql.insertId(db)
proc insertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the
proc insertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery,
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [FReadDB, FWriteDb].} =
tags: [FReadDB, FWriteDb].} =
## runs the query (typically "UPDATE") and returns the
## number of affected rows
rawExec(db, query, args)
result = mysql.affectedRows(db)
proc close*(db: DbConn) {.tags: [FDb].} =
proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection.
if db != nil: mysql.close(db)
@@ -242,14 +242,14 @@ proc open*(connection, user, password, database: string): DbConn {.
## opens a database connection. Raises `EDb` if the connection could not
## be established.
result = mysql.init(nil)
if result == nil: dbError("could not open database connection")
if result == nil: dbError("could not open database connection")
let
colonPos = connection.find(':')
host = if colonPos < 0: connection
else: substr(connection, 0, colonPos-1)
port: int32 = if colonPos < 0: 0'i32
else: substr(connection, colonPos+1).parseInt.int32
if mysql.realConnect(result, host, user, password, database,
if mysql.realConnect(result, host, user, password, database,
port, nil, 0) == nil:
var errmsg = $mysql.error(result)
db_mysql.close(result)
@@ -257,6 +257,6 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} =
## sets the encoding of a database connection, returns true for
## sets the encoding of a database connection, returns true for
## success, false for failure.
result = mysql.set_character_set(connection, encoding) == 0

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## A higher level `PostgreSQL`:idx: database wrapper. This interface
## A higher level `PostgreSQL`:idx: database wrapper. This interface
## is implemented for other databases too.
import strutils, postgres
@@ -20,7 +20,7 @@ type
## used to get a row's
## column text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string
SqlPrepared* = distinct string ## a identifier for the prepared queries
@@ -30,15 +30,15 @@ type
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
TSqlPrepared: SqlPrepared].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
result = SqlQuery(query)
proc dbError*(db: DbConn) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
@@ -73,7 +73,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
inc(a)
else:
add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise.
@@ -106,7 +106,7 @@ proc exec*(db: DbConn, stmtName: SqlPrepared,
proc newRow(L: int): Row =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): PPGresult =
var arr = allocCStringArray(args)
@@ -128,7 +128,7 @@ proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
return SqlPrepared(stmtName)
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
for col in 0..cols-1:
setLen(r[col], 0)
@@ -140,7 +140,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the query and iterates over the result dataset. This is very
## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Postgres it is safe though.
var res = setupQuery(db, query, args)
@@ -224,14 +224,14 @@ proc getValue*(db: DbConn, query: SqlQuery,
## value is NULL.
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
result = if isNil(x): "" else: $x
proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
## executes the query (typically "INSERT") and returns the
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``.
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
## named ``id``.
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
args), 0, 0)
if not isNil(x):
result = parseBiggestInt($x)
@@ -240,13 +240,13 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the
## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``.
## named ``id``.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [
FReadDB, FWriteDb].} =
@@ -286,6 +286,6 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} =
## sets the encoding of a database connection, returns true for
## sets the encoding of a database connection, returns true for
## success, false for failure.
return pqsetClientEncoding(connection, encoding) == 0

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## A higher level `SQLite`:idx: database wrapper. This interface
## A higher level `SQLite`:idx: database wrapper. This interface
## is implemented for other databases too.
import strutils, sqlite3
@@ -19,31 +19,31 @@ type
InstantRow* = Pstmt ## a handle that can be used to get a row's column
## text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} =
proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
new(e)
e.msg = $sqlite3.errmsg(db)
raise e
proc dbError*(msg: string) {.noreturn.} =
proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`.
var e: ref EDb
new(e)
@@ -67,8 +67,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
inc(a)
else:
add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery,
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args)
@@ -81,32 +81,32 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDb, FWriteDb].} =
## executes the query and raises EDB if not successful.
if not tryExec(db, query, args): dbError(db)
proc newRow(L: int): Row =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt =
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt =
var q = dbFormat(query, args)
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
for col in 0..cols-1:
setLen(r[col], column_bytes(stmt, col)) # set capacity
setLen(r[col], 0)
let x = column_text(stmt, col)
if not isNil(x): add(r[col], x)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## executes the query and iterates over the result dataset. This is very
## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Sqlite it is safe though.
var stmt = setupQuery(db, query, args)
var L = (column_count(stmt))
var result = newRow(L)
while step(stmt) == SQLITE_ROW:
while step(stmt) == SQLITE_ROW:
setRow(stmt, result, L)
yield result
if finalize(stmt) != SQLITE_OK: dbError(db)
@@ -136,31 +136,31 @@ proc getRow*(db: DbConn, query: SqlQuery,
var stmt = setupQuery(db, query, args)
var L = (column_count(stmt))
result = newRow(L)
if step(stmt) == SQLITE_ROW:
if step(stmt) == SQLITE_ROW:
setRow(stmt, result, L)
if finalize(stmt) != SQLITE_OK: dbError(db)
proc getAllRows*(db: DbConn, query: SqlQuery,
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
## executes the query and returns the whole result dataset.
result = @[]
for r in fastRows(db, query, args):
result.add(r)
iterator rows*(db: DbConn, query: SqlQuery,
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## same as `FastRows`, but slower and safe.
for r in fastRows(db, query, args): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
var stmt = setupQuery(db, query, args)
if step(stmt) == SQLITE_ROW:
let cb = column_bytes(stmt, 0)
if cb == 0:
if cb == 0:
result = ""
else:
result = newStringOfCap(cb)
@@ -168,12 +168,12 @@ proc getValue*(db: DbConn, query: SqlQuery,
else:
result = ""
if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: DbConn, query: SqlQuery,
proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt
result = -1
@@ -183,27 +183,27 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
if finalize(stmt) != SQLITE_OK:
result = -1
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``.
## named ``id``.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery,
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [FReadDb, FWriteDb].} =
tags: [FReadDb, FWriteDb].} =
## executes the query (typically "UPDATE") and returns the
## number of affected rows.
exec(db, query, args)
result = changes(db)
proc close*(db: DbConn) {.tags: [FDb].} =
proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection.
if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} =
## opens a database connection. Raises `EDb` if the connection could not
@@ -216,12 +216,12 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} =
## sets the encoding of a database connection, returns true for
## sets the encoding of a database connection, returns true for
## success, false for failure.
##
## Note that the encoding cannot be changed once it's been set.
## According to SQLite3 documentation, any attempt to change
## the encoding after the database is created will be silently
## According to SQLite3 documentation, any attempt to change
## the encoding after the database is created will be silently
## ignored.
exec(connection, sql"PRAGMA encoding = ?", [encoding])
result = connection.getValue(sql"PRAGMA encoding") == encoding

View File

@@ -9,7 +9,7 @@
## This module implements graphical output for Nim; the current
## implementation uses SDL but the interface is meant to support multiple
## backends some day. There is no need to init SDL as this module does that
## backends some day. There is no need to init SDL as this module does that
## implicitly.
import colors, math
@@ -24,7 +24,7 @@ type
Surface* {.pure, final.} = object
w*, h*: Natural
s*: sdl.PSurface
EGraphics* = object of IOError
Font {.pure, final.} = object
@@ -35,7 +35,7 @@ type
proc toSdlColor*(c: Color): sdl.Color =
## Convert colors.Color to sdl.Color
var x = c.extractRGB
var x = c.extractRGB
result.r = x.r and 0xff
result.g = x.g and 0xff
result.b = x.b and 0xff
@@ -43,7 +43,7 @@ proc toSdlColor*(c: Color): sdl.Color =
proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
## Creates a color using ``sdl.MapRGBA``.
var x = c.extractRGB
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
x.b and 0xff, alpha and 0xff)
proc toSdlRect*(r: Rect): sdl.Rect =
@@ -53,26 +53,26 @@ proc toSdlRect*(r: Rect): sdl.Rect =
result.w = uint16(r.width)
result.h = uint16(r.height)
proc raiseEGraphics =
proc raiseEGraphics =
raise newException(EGraphics, $sdl.getError())
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
proc newSurface*(width, height: int): PSurface =
## creates a new surface.
new(result, surfaceFinalizer)
result.w = width
result.h = height
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
if result.s == nil:
raiseEGraphics()
assert(not sdl.mustLock(result.s))
proc fontFinalizer(f: PFont) = closeFont(f.f)
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
new(result, fontFinalizer)
result.f = openFont(name, size.cint)
@@ -84,7 +84,7 @@ var
defaultFont*: PFont ## default font that is used; this needs to initialized
## by the client!
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
## initializes the `defaultFont` var.
defaultFont = newFont(name, size, color)
@@ -98,7 +98,7 @@ proc newScreenSurface*(width, height: int): PSurface =
raiseEGraphics()
proc writeToBMP*(sur: PSurface, filename: string) =
## Saves the contents of the surface `sur` to the file `filename` as a
## Saves the contents of the surface `sur` to the file `filename` as a
## BMP file.
if sdl.saveBMP(sur.s, filename) != 0:
raise newException(IOError, "cannot write: " & filename)
@@ -111,7 +111,7 @@ type
template setPix(video, pitch, x, y, col: expr): stmt =
video[y * pitch + x] = int32(col)
template getPix(video, pitch, x, y: expr): expr =
template getPix(video, pitch, x, y: expr): expr =
colors.Color(video[y * pitch + x])
const
@@ -120,7 +120,7 @@ const
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
assert x <% sur.w
assert y <% sur.h
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
x, y)
proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
@@ -146,7 +146,7 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
## set the pixel at position ``(x, y)``. No range checking is done!
setPixel(sur, x, y, col)
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
srcRect: Rect) =
## Copies ``srcSurf`` into ``destSurf``
var destTRect, srcTRect: sdl.Rect
@@ -175,7 +175,7 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
## font.
var textSur: PSurface # This surface will have the text drawn on it
new(textSur, surfaceFinalizer)
# Render the text
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
# Merge the text surface with sur
@@ -183,14 +183,14 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
proc drawText*(sur: PSurface, p: Point, text: string,
bg: Color, font = defaultFont) =
## Draws text, at location ``p`` with font ``font``. ``bg``
## Draws text, at location ``p`` with font ``font``. ``bg``
## is the background color.
var textSur: PSurface # This surface will have the text drawn on it
new(textSur, surfaceFinalizer)
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
# Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur`.
@@ -205,7 +205,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
if x+px <% sur.w:
if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color)
if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color)
if x-px <% sur.w:
if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color)
if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color)
@@ -213,7 +213,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
if x+py <% sur.w:
if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color)
if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color)
if x-py <% sur.w:
if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color)
if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color)
@@ -225,10 +225,10 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
py = py - 1
px = px + 1
proc `>-<`(val: int, s: PSurface): int {.inline.} =
proc `>-<`(val: int, s: PSurface): int {.inline.} =
return if val < 0: 0 elif val >= s.w: s.w-1 else: val
proc `>|<`(val: int, s: PSurface): int {.inline.} =
proc `>|<`(val: int, s: PSurface): int {.inline.} =
return if val < 0: 0 elif val >= s.h: s.h-1 else: val
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
@@ -242,7 +242,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
var dy = y1 - y0
var dx = x1 - x0
if dy < 0:
dy = -dy
dy = -dy
stepy = -1
else:
stepy = 1
@@ -251,7 +251,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
stepx = -1
else:
stepx = 1
dy = dy * 2
dy = dy * 2
dx = dx * 2
var video = cast[PPixels](sur.s.pixels)
var pitch = sur.s.pitch.int div ColSize
@@ -328,17 +328,17 @@ proc drawRect*(sur: PSurface, r: Rect, color: Color) =
if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h):
var minW = min(sur.s.w - r.x, r.width)
var minH = min(sur.s.h - r.y, r.height)
# Draw Top
for i in 0 .. minW - 1:
setPix(video, pitch, r.x + i, r.y, color)
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
# Draw left side
for i in 0 .. minH - 1:
setPix(video, pitch, r.x, r.y + i, color)
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
## Fills a rectangle using sdl's ``FillRect`` function.
var rect = toSdlRect(r)
@@ -350,23 +350,23 @@ proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) =
var pitch = sur.s.pitch.int div ColSize
if cx+x <= sur.s.w-1:
if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col)
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
if cx-x <= sur.s.w-1:
if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col)
if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col)
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
col: Color) =
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
## of the ellipse.
var
var
x, y: Natural
xChange, yChange: int
ellipseError: Natural
twoASquare, twoBSquare: Natural
stoppingX, stoppingY: Natural
twoASquare = 2 * xRadius * xRadius
twoBSquare = 2 * yRadius * yRadius
x = xRadius
@@ -376,7 +376,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
ellipseError = 0
stoppingX = twoBSquare * xRadius
stoppingY = 0
while stoppingX >= stoppingY: # 1st set of points, y` > - 1
sur.plot4EllipsePoints(cx, cy, x, y, col)
inc(y)
@@ -388,7 +388,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
dec(stoppingX, twoBSquare)
inc(ellipseError, xChange)
inc(xChange, twoBSquare)
# 1st point set is done; start the 2nd set of points
x = 0
y = yRadius
@@ -408,7 +408,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
dec(stoppingY, twoASquare)
inc(ellipseError, yChange)
inc(yChange,twoASquare)
proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h):
@@ -419,43 +419,43 @@ proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
setPix(video, pitch, x, y,
pixColor.intensity(1.0 - c) + color.intensity(c))
template ipart(x: expr): expr = floor(x)
template ipart(x: expr): expr = floor(x)
template cround(x: expr): expr = ipart(x + 0.5)
template fpart(x: expr): expr = x - ipart(x)
template rfpart(x: expr): expr = 1.0 - fpart(x)
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
## line algorithm
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
p1.y.toFloat(), p2.y.toFloat())
var dx = x2 - x1
var dy = y2 - y1
var ax = dx
if ax < 0'f64:
ax = 0'f64 - ax
var ay = dy
if ay < 0'f64:
ay = 0'f64 - ay
if ax < ay:
swap(x1, y1)
swap(x2, y2)
swap(dx, dy)
template doPlot(x, y: int, c: float, color: Color): stmt =
if ax < ay:
sur.plotAA(y, x, c, color)
else:
sur.plotAA(x, y, c, color)
if x2 < x1:
swap(x1, x2)
swap(y1, y2)
var gradient = dy / dx
# handle first endpoint
var xend = cround(x1)
@@ -509,19 +509,19 @@ when not defined(testing) and isMainModule:
# Draw the shapes
surf.drawLineAA((150, 170), (400, 471), colTan)
surf.drawLine((100, 170), (400, 471), colRed)
surf.drawEllipse(200, 300, 200, 30, colSeaGreen)
surf.drawHorLine(1, 300, 400, colViolet)
surf.drawHorLine(1, 300, 400, colViolet)
# Check if the ellipse is the size it's suppose to be.
surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
surf.drawEllipse(400, 300, 300, 300, colOrange)
surf.drawEllipse(5, 5, 5, 5, colGreen)
surf.drawHorLine(5, 5, 900, colRed)
surf.drawVerLine(5, 60, 800, colRed)
surf.drawCircle((600, 500), 60, colRed)
surf.fillRect((50, 50, 100, 100), colFuchsia)
surf.fillRect((150, 50, 100, 100), colGreen)
surf.drawRect((50, 150, 100, 100), colGreen)
@@ -530,12 +530,12 @@ when not defined(testing) and isMainModule:
surf.drawHorLine(250, 150, 100, colRed)
surf.drawLineAA((592, 160), (592, 280), colPurple)
#surf.drawText((300, 300), "TEST", colMidnightBlue)
#var textSize = textBounds("TEST")
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
# $textSize.height, colDarkGreen)
var mouseStartX = -1
var mouseStartY = -1
withEvents(surf, event):
@@ -561,17 +561,17 @@ when not defined(testing) and isMainModule:
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
mouseStartX = -1
mouseStartY = -1
of sdl.MOUSEMOTION:
var mm = sdl.evMouseMotion(eventp)
if mouseStartX != -1 and mouseStartY != -1:
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
#echo(mm.x, " ", mm.y, " ", mm.yrel)
else:
discard "echo(event.kind)"
sdl.updateRect(surf.s, 0, 0, 800, 600)
surf.writeToBMP("test.bmp")
sdl.quit()

View File

@@ -41,11 +41,11 @@ type
reExtended = 3, ## ignore whitespace and ``#`` comments
reStudy = 4 ## study the expression (may be omitted if the
## expression will be used only once)
RegexDesc = object
RegexDesc = object
h: ptr Pcre
e: ptr ExtraData
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
RegexError* = object of ValueError

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## This module provides an easy to use sockets-style
## This module provides an easy to use sockets-style
## nim interface to the OpenSSL library.
{.deprecated.}
@@ -20,37 +20,37 @@ type
bio: BIO
{.deprecated: [TSecureSocket: SecureSocket].}
proc connect*(sock: var SecureSocket, address: string,
proc connect*(sock: var SecureSocket, address: string,
port: int): int =
## Connects to the specified `address` on the specified `port`.
## Returns the result of the certificate validation.
SslLoadErrorStrings()
ERR_load_BIO_strings()
if SSL_library_init() != 1:
raiseOSError(osLastError())
var ctx = SSL_CTX_new(SSLv23_client_method())
if ctx == nil:
ERR_print_errors_fp(stderr)
raiseOSError(osLastError())
#if SSL_CTX_load_verify_locations(ctx,
#if SSL_CTX_load_verify_locations(ctx,
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
# echo("Failed load verify locations")
# ERR_print_errors_fp(stderr)
sock.bio = BIO_new_ssl_connect(ctx)
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
raiseOSError(osLastError())
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
raiseOSError(osLastError())
if BIO_do_connect(sock.bio) <= 0:
ERR_print_errors_fp(stderr)
raiseOSError(osLastError())
result = SSL_get_verify_result(sock.ssl)
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
@@ -86,12 +86,12 @@ proc close*(sock: SecureSocket) =
when not defined(testing) and isMainModule:
var s: SecureSocket
echo connect(s, "smtp.gmail.com", 465)
#var buffer: array[0..255, char]
#echo BIO_read(bio, buffer, buffer.len)
var buffer: string = ""
echo s.recvLine(buffer)
echo buffer
echo buffer
echo buffer.len

View File

@@ -7,8 +7,8 @@
# distribution, for details about the copyright.
#
## Main file to generate a DLL from the standard library.
## The default Nimrtl does not only contain the ``system`` module, but these
## Main file to generate a DLL from the standard library.
## The default Nimrtl does not only contain the ``system`` module, but these
## too:
##
## * parseutils
@@ -22,12 +22,12 @@
## * unicode
## * pegs
## * ropes
##
##
when system.appType != "lib":
{.error: "This file has to be compiled as a library!".}
when not defined(createNimRtl):
when not defined(createNimRtl):
{.error: "This file has to be compiled with '-d:createNimRtl'".}
import

View File

@@ -29,34 +29,34 @@ const
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
const
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
const
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
type
epoll_data* {.importc: "union epoll_data",
type
epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
#thePtr* {.importc: "ptr".}: pointer
fd* {.importc: "fd".}: cint # \
#u32*: uint32
#u64*: uint64
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
events*: uint32 # Epoll events
data*: epoll_data # User data variable
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
events*: uint32 # Epoll events
data*: epoll_data # User data variable
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
header: "<sys/epoll.h>".}
## Creates an epoll instance. Returns an fd for the new instance.
## The "size" parameter is a hint specifying the number of file
## descriptors to be associated with the new instance. The fd
## returned by epoll_create() should be closed with close().
## returned by epoll_create() should be closed with close().
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
header: "<sys/epoll.h>".}
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped.
## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".}
@@ -65,10 +65,10 @@ proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
## constants defined above. The "fd" parameter is the target of the
## operation. The "event" parameter describes which events the caller
## is interested in and any associated user data.
## is interested in and any associated user data.
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint {.importc: "epoll_wait",
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint {.importc: "epoll_wait",
header: "<sys/epoll.h>".}
## Wait for events on an epoll instance "epfd". Returns the number of
## triggered events returned in "events" buffer. Or -1 in case of
@@ -82,11 +82,11 @@ proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
## __THROW.
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
# timeout: cint; ss: ptr sigset_t): cint {.
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
# Same as epoll_wait, but the thread's signal mask is temporarily
# and atomically replaced with the one provided as parameter.
#
# This function is a cancellation point and therefore not marked with
# __THROW.
# __THROW.

View File

@@ -9,65 +9,65 @@
{.deadCodeElim:on.}
# Get the platform-dependent flags.
# Structure describing an inotify event.
type
InotifyEvent*{.pure, final, importc: "struct inotify_event",
header: "<sys/inotify.h>".} = object
wd*{.importc: "wd".}: cint # Watch descriptor.
mask*{.importc: "mask".}: uint32 # Watch mask.
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
name*{.importc: "name".}: char # Name.
# Get the platform-dependent flags.
# Structure describing an inotify event.
type
InotifyEvent*{.pure, final, importc: "struct inotify_event",
header: "<sys/inotify.h>".} = object
wd*{.importc: "wd".}: cint # Watch descriptor.
mask*{.importc: "mask".}: uint32 # Watch mask.
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
name*{.importc: "name".}: char # Name.
{.deprecated: [Tinotify_event: InotifyEvent].}
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
const
IN_ACCESS* = 0x00000001 # File was accessed.
IN_MODIFY* = 0x00000002 # File was modified.
IN_ATTRIB* = 0x00000004 # Metadata changed.
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
IN_OPEN* = 0x00000020 # File was opened.
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
IN_CREATE* = 0x00000100 # Subfile was created.
IN_DELETE* = 0x00000200 # Subfile was deleted.
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
IN_MOVE_SELF* = 0x00000800 # Self was moved.
# Events sent by the kernel.
const
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
IN_IGNORED* = 0x00008000 # File was ignored.
# Special flags.
const
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
const
IN_ACCESS* = 0x00000001 # File was accessed.
IN_MODIFY* = 0x00000002 # File was modified.
IN_ATTRIB* = 0x00000004 # Metadata changed.
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
IN_OPEN* = 0x00000020 # File was opened.
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
IN_CREATE* = 0x00000100 # Subfile was created.
IN_DELETE* = 0x00000200 # Subfile was deleted.
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
IN_MOVE_SELF* = 0x00000800 # Self was moved.
# Events sent by the kernel.
const
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
IN_IGNORED* = 0x00008000 # File was ignored.
# Special flags.
const
IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a
# directory.
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
# directory.
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
# objects.
# objects.
IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
# existing watch.
IN_ISDIR* = 0x40000000 # Event occurred against dir.
IN_ONESHOT* = 0x80000000 # Only send event once.
# All events which a program can wait on.
const
# existing watch.
IN_ISDIR* = 0x40000000 # Event occurred against dir.
IN_ONESHOT* = 0x80000000 # Only send event once.
# All events which a program can wait on.
const
IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or
IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or
IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
# Create and initialize inotify instance.
proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
header: "<sys/inotify.h>".}
# Create and initialize inotify instance.
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
# Create and initialize inotify instance.
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
header: "<sys/inotify.h>".}
# Add watch of object NAME to inotify instance FD. Notify about
# events specified by MASK.
# events specified by MASK.
proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
# Remove the watch specified by WD from the inotify instance FD.
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
# Remove the watch specified by WD from the inotify instance FD.
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}

View File

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

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")

View File

@@ -13,20 +13,20 @@
{.push hints:off}
proc c_strcmp(a, b: cstring): cint {.header: "<string.h>",
proc c_strcmp(a, b: cstring): cint {.header: "<string.h>",
noSideEffect, importc: "strcmp".}
proc c_memcmp(a, b: cstring, size: int): cint {.header: "<string.h>",
proc c_memcmp(a, b: cstring, size: int): cint {.header: "<string.h>",
noSideEffect, importc: "memcmp".}
proc c_memcpy(a, b: cstring, size: int) {.header: "<string.h>", importc: "memcpy".}
proc c_strlen(a: cstring): int {.header: "<string.h>",
proc c_strlen(a: cstring): int {.header: "<string.h>",
noSideEffect, importc: "strlen".}
proc c_memset(p: pointer, value: cint, size: int) {.
header: "<string.h>", importc: "memset".}
type
C_TextFile {.importc: "FILE", header: "<stdio.h>",
C_TextFile {.importc: "FILE", header: "<stdio.h>",
final, incompleteStruct.} = object
C_BinaryFile {.importc: "FILE", header: "<stdio.h>",
C_BinaryFile {.importc: "FILE", header: "<stdio.h>",
final, incompleteStruct.} = object
C_TextFileStar = ptr C_TextFile
C_BinaryFileStar = ptr C_BinaryFile
@@ -101,15 +101,15 @@ proc c_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.
importc: "signal", header: "<signal.h>".}
proc c_raise(sig: cint) {.importc: "raise", header: "<signal.h>".}
proc c_fputs(c: cstring, f: C_TextFileStar) {.importc: "fputs",
proc c_fputs(c: cstring, f: C_TextFileStar) {.importc: "fputs",
header: "<stdio.h>".}
proc c_fgets(c: cstring, n: int, f: C_TextFileStar): cstring {.
importc: "fgets", header: "<stdio.h>".}
proc c_fgetc(stream: C_TextFileStar): int {.importc: "fgetc",
proc c_fgetc(stream: C_TextFileStar): int {.importc: "fgetc",
header: "<stdio.h>".}
proc c_ungetc(c: int, f: C_TextFileStar) {.importc: "ungetc",
proc c_ungetc(c: int, f: C_TextFileStar) {.importc: "ungetc",
header: "<stdio.h>".}
proc c_putc(c: char, stream: C_TextFileStar) {.importc: "putc",
proc c_putc(c: char, stream: C_TextFileStar) {.importc: "putc",
header: "<stdio.h>".}
proc c_fprintf(f: C_TextFileStar, frmt: cstring) {.
importc: "fprintf", header: "<stdio.h>", varargs.}
@@ -120,7 +120,7 @@ proc c_fopen(filename, mode: cstring): C_TextFileStar {.
importc: "fopen", header: "<stdio.h>".}
proc c_fclose(f: C_TextFileStar) {.importc: "fclose", header: "<stdio.h>".}
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs, noSideEffect.}
# we use it only in a way that cannot lead to security issues
@@ -149,7 +149,7 @@ when hostOS != "standalone":
when not declared(errno):
when defined(NimrodVM):
var vmErrnoWrapper {.importc.}: ptr cint
template errno: expr =
template errno: expr =
bind vmErrnoWrapper
vmErrnoWrapper[]
else:

View File

@@ -16,7 +16,7 @@ proc lowGauge(n: PAvlNode): int =
while not isBottom(it):
result = it.key
it = it.link[0]
proc highGauge(n: PAvlNode): int =
result = -1
var it = n
@@ -24,7 +24,7 @@ proc highGauge(n: PAvlNode): int =
result = it.upperBound
it = it.link[1]
proc find(root: PAvlNode, key: int): PAvlNode =
proc find(root: PAvlNode, key: int): PAvlNode =
var it = root
while not isBottom(it):
if it.key == key: return it

View File

@@ -65,7 +65,7 @@ proc init(s: var CellSeq, cap: int = 1024) =
s.cap = cap
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
proc deinit(s: var CellSeq) =
proc deinit(s: var CellSeq) =
dealloc(s.d)
s.d = nil
s.len = 0
@@ -98,7 +98,7 @@ proc nextTry(h, maxHash: int): int {.inline.} =
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
var h = cast[int](key) and t.max
while t.data[h] != nil:
@@ -170,16 +170,16 @@ proc excl(s: var CellSet, cell: PCell) =
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
not (1 shl (u and IntMask)))
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlign
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
if not result:
if not result:
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
(1 shl (u and IntMask))
else:
else:
incl(s, cell)
result = false

View File

@@ -1,267 +1,267 @@
#
#
# Nim's Runtime Library
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Channel support for threads. **Note**: This is part of the system module.
## Do not import it directly. To activate thread support you need to compile
## with the ``--threads:on`` command line switch.
##
## **Note:** The current implementation of message passing is slow and does
## not work with cyclic data structures.
when not declared(NimString):
{.error: "You must not import this module explicitly".}
type
pbytes = ptr array[0.. 0xffff, byte]
RawChannel {.pure, final.} = object ## msg queue for a thread
rd, wr, count, mask: int
data: pbytes
lock: SysLock
cond: SysCond
elemType: PNimType
ready: bool
region: MemRegion
PRawChannel = ptr RawChannel
LoadStoreMode = enum mStore, mLoad
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Channel support for threads. **Note**: This is part of the system module.
## Do not import it directly. To activate thread support you need to compile
## with the ``--threads:on`` command line switch.
##
## **Note:** The current implementation of message passing is slow and does
## not work with cyclic data structures.
when not declared(NimString):
{.error: "You must not import this module explicitly".}
type
pbytes = ptr array[0.. 0xffff, byte]
RawChannel {.pure, final.} = object ## msg queue for a thread
rd, wr, count, mask: int
data: pbytes
lock: SysLock
cond: SysCond
elemType: PNimType
ready: bool
region: MemRegion
PRawChannel = ptr RawChannel
LoadStoreMode = enum mStore, mLoad
Channel* {.gcsafe.}[TMsg] = RawChannel ## a channel for thread communication
{.deprecated: [TRawChannel: RawChannel, TLoadStoreMode: LoadStoreMode,
TChannel: Channel].}
const ChannelDeadMask = -2
proc initRawChannel(p: pointer) =
var c = cast[PRawChannel](p)
initSysLock(c.lock)
initSysCond(c.cond)
c.mask = -1
proc deinitRawChannel(p: pointer) =
var c = cast[PRawChannel](p)
# we need to grab the lock to be safe against sending threads!
acquireSys(c.lock)
c.mask = ChannelDeadMask
deallocOsPages(c.region)
deinitSys(c.lock)
deinitSysCond(c.cond)
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: LoadStoreMode) {.benign.}
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
mode: LoadStoreMode) {.benign.} =
var
d = cast[ByteAddress](dest)
s = cast[ByteAddress](src)
case n.kind
of nkSlot: storeAux(cast[pointer](d +% n.offset),
cast[pointer](s +% n.offset), n.typ, t, mode)
of nkList:
for i in 0..n.len-1: storeAux(dest, src, n.sons[i], t, mode)
of nkCase:
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ.size)
var m = selectBranch(src, n)
if m != nil: storeAux(dest, src, m, t, mode)
of nkNone: sysAssert(false, "storeAux")
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: LoadStoreMode) =
var
d = cast[ByteAddress](dest)
s = cast[ByteAddress](src)
sysAssert(mt != nil, "mt == nil")
case mt.kind
of tyString:
if mode == mStore:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
if s2 == nil:
x[] = nil
else:
var ss = cast[NimString](s2)
var ns = cast[NimString](alloc(t.region, ss.len+1 + GenericSeqSize))
copyMem(ns, ss, ss.len+1 + GenericSeqSize)
x[] = ns
else:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
if s2 == nil:
unsureAsgnRef(x, s2)
else:
unsureAsgnRef(x, copyString(cast[NimString](s2)))
dealloc(t.region, s2)
of tySequence:
var s2 = cast[PPointer](src)[]
var seq = cast[PGenericSeq](s2)
var x = cast[PPointer](dest)
if s2 == nil:
if mode == mStore:
x[] = nil
else:
unsureAsgnRef(x, nil)
else:
sysAssert(dest != nil, "dest == nil")
if mode == mStore:
x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize)
else:
unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize))
var dst = cast[ByteAddress](cast[PPointer](dest)[])
for i in 0..seq.len-1:
storeAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
mt.base, t, mode)
var dstseq = cast[PGenericSeq](dst)
dstseq.len = seq.len
dstseq.reserved = seq.len
if mode != mStore: dealloc(t.region, s2)
of tyObject:
# copy type field:
var pint = cast[ptr PNimType](dest)
# XXX use dynamic type here!
pint[] = mt
if mt.base != nil:
storeAux(dest, src, mt.base, t, mode)
storeAux(dest, src, mt.node, t, mode)
of tyTuple:
storeAux(dest, src, mt.node, t, mode)
of tyArray, tyArrayConstr:
for i in 0..(mt.size div mt.base.size)-1:
storeAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
of tyRef:
var s = cast[PPointer](src)[]
var x = cast[PPointer](dest)
if s == nil:
if mode == mStore:
x[] = nil
else:
unsureAsgnRef(x, nil)
else:
if mode == mStore:
x[] = alloc(t.region, mt.base.size)
else:
# XXX we should use the dynamic type here too, but that is not stored
# in the inbox at all --> use source[]'s object type? but how? we need
# a tyRef to the object!
var obj = newObj(mt, mt.base.size)
unsureAsgnRef(x, obj)
storeAux(x[], s, mt.base, t, mode)
if mode != mStore: dealloc(t.region, s)
else:
copyMem(dest, src, mt.size) # copy raw bits
proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
## adds an `item` to the end of the queue `q`.
var cap = q.mask+1
if q.count >= cap:
# start with capacity for 2 entries in the queue:
if cap == 0: cap = 1
var n = cast[pbytes](alloc0(q.region, cap*2*typ.size))
var z = 0
var i = q.rd
var c = q.count
while c > 0:
dec c
copyMem(addr(n[z*typ.size]), addr(q.data[i*typ.size]), typ.size)
i = (i + 1) and q.mask
inc z
if q.data != nil: dealloc(q.region, q.data)
q.data = n
q.mask = cap*2 - 1
q.wr = q.count
q.rd = 0
storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
inc q.count
q.wr = (q.wr + 1) and q.mask
proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
sysAssert q.count > 0, "rawRecv"
dec q.count
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
q.rd = (q.rd + 1) and q.mask
template lockChannel(q: expr, action: stmt) {.immediate.} =
acquireSys(q.lock)
action
releaseSys(q.lock)
template sendImpl(q: expr) {.immediate.} =
if q.mask == ChannelDeadMask:
sysFatal(DeadThreadError, "cannot send message; thread died")
acquireSys(q.lock)
var m: TMsg
shallowCopy(m, msg)
var typ = cast[PNimType](getTypeInfo(msg))
rawSend(q, addr(m), typ)
q.elemType = typ
releaseSys(q.lock)
signalSysCond(q.cond)
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
## sends a message to a thread. `msg` is deeply copied.
var q = cast[PRawChannel](addr(c))
sendImpl(q)
proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
# to save space, the generic is as small as possible
q.ready = true
while q.count <= 0:
waitSysCond(q.cond, q.lock)
q.ready = false
if typ != q.elemType:
releaseSys(q.lock)
sysFatal(ValueError, "cannot receive message of wrong type")
rawRecv(q, res, typ)
proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
## receives a message from the channel `c`. This blocks until
## a message has arrived! You may use ``peek`` to avoid the blocking.
var q = cast[PRawChannel](addr(c))
acquireSys(q.lock)
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
releaseSys(q.lock)
proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
msg: TMsg] =
## try to receives a message from the channel `c` if available. Otherwise
## it returns ``(false, default(msg))``.
var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask:
const ChannelDeadMask = -2
proc initRawChannel(p: pointer) =
var c = cast[PRawChannel](p)
initSysLock(c.lock)
initSysCond(c.cond)
c.mask = -1
proc deinitRawChannel(p: pointer) =
var c = cast[PRawChannel](p)
# we need to grab the lock to be safe against sending threads!
acquireSys(c.lock)
c.mask = ChannelDeadMask
deallocOsPages(c.region)
deinitSys(c.lock)
deinitSysCond(c.cond)
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: LoadStoreMode) {.benign.}
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
mode: LoadStoreMode) {.benign.} =
var
d = cast[ByteAddress](dest)
s = cast[ByteAddress](src)
case n.kind
of nkSlot: storeAux(cast[pointer](d +% n.offset),
cast[pointer](s +% n.offset), n.typ, t, mode)
of nkList:
for i in 0..n.len-1: storeAux(dest, src, n.sons[i], t, mode)
of nkCase:
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ.size)
var m = selectBranch(src, n)
if m != nil: storeAux(dest, src, m, t, mode)
of nkNone: sysAssert(false, "storeAux")
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: LoadStoreMode) =
var
d = cast[ByteAddress](dest)
s = cast[ByteAddress](src)
sysAssert(mt != nil, "mt == nil")
case mt.kind
of tyString:
if mode == mStore:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
if s2 == nil:
x[] = nil
else:
var ss = cast[NimString](s2)
var ns = cast[NimString](alloc(t.region, ss.len+1 + GenericSeqSize))
copyMem(ns, ss, ss.len+1 + GenericSeqSize)
x[] = ns
else:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
if s2 == nil:
unsureAsgnRef(x, s2)
else:
unsureAsgnRef(x, copyString(cast[NimString](s2)))
dealloc(t.region, s2)
of tySequence:
var s2 = cast[PPointer](src)[]
var seq = cast[PGenericSeq](s2)
var x = cast[PPointer](dest)
if s2 == nil:
if mode == mStore:
x[] = nil
else:
unsureAsgnRef(x, nil)
else:
sysAssert(dest != nil, "dest == nil")
if mode == mStore:
x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize)
else:
unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize))
var dst = cast[ByteAddress](cast[PPointer](dest)[])
for i in 0..seq.len-1:
storeAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
mt.base, t, mode)
var dstseq = cast[PGenericSeq](dst)
dstseq.len = seq.len
dstseq.reserved = seq.len
if mode != mStore: dealloc(t.region, s2)
of tyObject:
# copy type field:
var pint = cast[ptr PNimType](dest)
# XXX use dynamic type here!
pint[] = mt
if mt.base != nil:
storeAux(dest, src, mt.base, t, mode)
storeAux(dest, src, mt.node, t, mode)
of tyTuple:
storeAux(dest, src, mt.node, t, mode)
of tyArray, tyArrayConstr:
for i in 0..(mt.size div mt.base.size)-1:
storeAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
of tyRef:
var s = cast[PPointer](src)[]
var x = cast[PPointer](dest)
if s == nil:
if mode == mStore:
x[] = nil
else:
unsureAsgnRef(x, nil)
else:
if mode == mStore:
x[] = alloc(t.region, mt.base.size)
else:
# XXX we should use the dynamic type here too, but that is not stored
# in the inbox at all --> use source[]'s object type? but how? we need
# a tyRef to the object!
var obj = newObj(mt, mt.base.size)
unsureAsgnRef(x, obj)
storeAux(x[], s, mt.base, t, mode)
if mode != mStore: dealloc(t.region, s)
else:
copyMem(dest, src, mt.size) # copy raw bits
proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
## adds an `item` to the end of the queue `q`.
var cap = q.mask+1
if q.count >= cap:
# start with capacity for 2 entries in the queue:
if cap == 0: cap = 1
var n = cast[pbytes](alloc0(q.region, cap*2*typ.size))
var z = 0
var i = q.rd
var c = q.count
while c > 0:
dec c
copyMem(addr(n[z*typ.size]), addr(q.data[i*typ.size]), typ.size)
i = (i + 1) and q.mask
inc z
if q.data != nil: dealloc(q.region, q.data)
q.data = n
q.mask = cap*2 - 1
q.wr = q.count
q.rd = 0
storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
inc q.count
q.wr = (q.wr + 1) and q.mask
proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
sysAssert q.count > 0, "rawRecv"
dec q.count
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
q.rd = (q.rd + 1) and q.mask
template lockChannel(q: expr, action: stmt) {.immediate.} =
acquireSys(q.lock)
action
releaseSys(q.lock)
template sendImpl(q: expr) {.immediate.} =
if q.mask == ChannelDeadMask:
sysFatal(DeadThreadError, "cannot send message; thread died")
acquireSys(q.lock)
var m: TMsg
shallowCopy(m, msg)
var typ = cast[PNimType](getTypeInfo(msg))
rawSend(q, addr(m), typ)
q.elemType = typ
releaseSys(q.lock)
signalSysCond(q.cond)
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
## sends a message to a thread. `msg` is deeply copied.
var q = cast[PRawChannel](addr(c))
sendImpl(q)
proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
# to save space, the generic is as small as possible
q.ready = true
while q.count <= 0:
waitSysCond(q.cond, q.lock)
q.ready = false
if typ != q.elemType:
releaseSys(q.lock)
sysFatal(ValueError, "cannot receive message of wrong type")
rawRecv(q, res, typ)
proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
## receives a message from the channel `c`. This blocks until
## a message has arrived! You may use ``peek`` to avoid the blocking.
var q = cast[PRawChannel](addr(c))
acquireSys(q.lock)
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
releaseSys(q.lock)
proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
msg: TMsg] =
## try to receives a message from the channel `c` if available. Otherwise
## it returns ``(false, default(msg))``.
var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask:
if tryAcquireSys(q.lock):
if q.count > 0:
llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
result.dataAvailable = true
releaseSys(q.lock)
proc peek*[TMsg](c: var Channel[TMsg]): int =
## returns the current number of messages in the channel `c`. Returns -1
## if the channel has been closed. **Note**: This is dangerous to use
## as it encourages races. It's much better to use ``tryRecv`` instead.
var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask:
lockChannel(q):
result = q.count
else:
result = -1
proc open*[TMsg](c: var Channel[TMsg]) =
## opens a channel `c` for inter thread communication.
initRawChannel(addr(c))
proc close*[TMsg](c: var Channel[TMsg]) =
## closes a channel `c` and frees its associated resources.
deinitRawChannel(addr(c))
proc ready*[TMsg](c: var Channel[TMsg]): bool =
## returns true iff some thread is waiting on the channel `c` for
## new messages.
var q = cast[PRawChannel](addr(c))
result = q.ready
if q.count > 0:
llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
result.dataAvailable = true
releaseSys(q.lock)
proc peek*[TMsg](c: var Channel[TMsg]): int =
## returns the current number of messages in the channel `c`. Returns -1
## if the channel has been closed. **Note**: This is dangerous to use
## as it encourages races. It's much better to use ``tryRecv`` instead.
var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask:
lockChannel(q):
result = q.count
else:
result = -1
proc open*[TMsg](c: var Channel[TMsg]) =
## opens a channel `c` for inter thread communication.
initRawChannel(addr(c))
proc close*[TMsg](c: var Channel[TMsg]) =
## closes a channel `c` and frees its associated resources.
deinitRawChannel(addr(c))
proc ready*[TMsg](c: var Channel[TMsg]): bool =
## returns true iff some thread is waiting on the channel `c` for
## new messages.
var q = cast[PRawChannel](addr(c))
result = q.ready

View File

@@ -14,7 +14,7 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
s = cast[ByteAddress](src)
case n.kind
of nkSlot:
genericDeepCopyAux(cast[pointer](d +% n.offset),
genericDeepCopyAux(cast[pointer](d +% n.offset),
cast[pointer](s +% n.offset), n.typ)
of nkList:
for i in 0..n.len-1:
@@ -24,7 +24,7 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
var m = selectBranch(src, n)
# reset if different branches are in use; note different branches also
# imply that's not self-assignment (``x = x``)!
if m != dd and dd != nil:
if m != dd and dd != nil:
genericResetAux(dest, dd)
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ.size)
@@ -103,16 +103,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
else:
let realType = x.typ
let z = newObj(realType, realType.base.size)
unsureAsgnRef(cast[PPointer](dest), z)
x.typ = cast[PNimType](cast[int](z) or 1)
genericDeepCopyAux(z, s2, realType.base)
x.typ = realType
else:
let realType = mt
let z = newObj(realType, realType.base.size)
let z = newObj(realType, realType.base.size)
unsureAsgnRef(cast[PPointer](dest), z)
genericDeepCopyAux(z, s2, realType.base)
genericDeepCopyAux(z, s2, realType.base)
of tyPtr:
# no cycle check here, but also not really required
let s2 = cast[PPointer](src)[]

View File

@@ -37,19 +37,19 @@ const
rcAlive = 0b00000 # object is reachable.
# color *black* in the original paper
rcCycleCandidate = 0b00001 # possible root of a cycle. *purple*
rcDecRefApplied = 0b00010 # the first dec-ref phase of the
# collector was already applied to this
# object. *gray*
rcMaybeDead = 0b00011 # this object is a candidate for deletion
# during the collect cycles algorithm.
# *white*.
rcReallyDead = 0b00100 # this is proved to be garbage
rcRetiredBuffer = 0b00101 # this is a seq or string buffer that
# was replaced by a resize operation.
# see growObj for details
@@ -80,14 +80,14 @@ const
# The bit must also be set for new objects that are not rc1 and it must be
# examined in the decref loop in collectCycles.
# XXX: not implemented yet as tests didn't show any improvement from this
MarkingSkipsAcyclicObjects = true
# Acyclic objects can be safely ignored in the mark and scan phases,
# Acyclic objects can be safely ignored in the mark and scan phases,
# because they cannot contribute to the internal count.
# XXX: if we generate specialized `markCyclic` and `markAcyclic`
# procs we can further optimize this as there won't be need for any
# checks in the code
MinimumStackMarking = false
# Try to scan only the user stack and ignore the part of the stack
# belonging to the GC itself. see setStackTop for further info.
@@ -110,9 +110,9 @@ type
maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size
maxStackCells: int # max stack cells in ``decStack``
cycleTableSize: int # max entries in cycle table
cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds
GcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table
stackBottom: pointer
@@ -124,7 +124,7 @@ type
tempStack: CellSeq # temporary stack for recursion elimination
freeStack: CellSeq # objects ready to be freed
recGcLock: int # prevent recursion via finalizers; no thread lock
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
# cycle roots table that uses a cheap linear scan
# to find only possitively dead objects.
# One strategy is to perform it only for new objects
@@ -143,11 +143,11 @@ var
when not defined(useNimRtl):
instantiateForRegion(gch.region)
template acquire(gch: GcHeap) =
template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap:
AcquireSys(HeapLock)
template release(gch: GcHeap) =
template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock)
@@ -185,7 +185,7 @@ when debugGC:
of rcRetiredBuffer: return "retired"
of rcReallyDead: return "dead"
else: return "unknown?"
proc inCycleRootsStr(c: PCell): cstring =
if c.isBitUp(rcInCycleRoots): result = "cycleroot"
else: result = ""
@@ -225,7 +225,7 @@ template setStackTop(gch) =
template addCycleRoot(cycleRoots: var CellSeq, c: PCell) =
if c.color != rcCycleCandidate:
c.setColor rcCycleCandidate
# the object may be buffered already. for example, consider:
# decref; incref; decref
if c.isBitDown(rcInCycleRoots):
@@ -307,7 +307,7 @@ when traceGC:
let startLen = gch.tempStack.len
c.forAllChildren waPush
while startLen != gch.tempStack.len:
dec gch.tempStack.len
var c = gch.tempStack.d[gch.tempStack.len]
@@ -331,7 +331,7 @@ when traceGC:
if c.isBitUp(rcMarkBit) and not isMarked:
writecell("cyclic cell", cell)
cprintf "Weight %d\n", cell.computeCellWeight
proc writeLeakage(onlyRoots: bool) =
if onlyRoots:
for c in elements(states[csAllocated]):
@@ -356,7 +356,7 @@ template WithHeapLock(blk: stmt): stmt =
blk
when hasThreadSupport and hasSharedHeap: ReleaseSys(HeapLock)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
# we MUST access gch as a global here, because this crosses DLL boundaries!
WithHeapLock: addCycleRoot(gch.cycleRoots, c)
@@ -423,7 +423,7 @@ template doIncRef(cc: PCell,
elif IncRefRemovesCandidates:
c.setColor rcAlive
# XXX: this is not really atomic enough!
proc nimGCref(p: pointer) {.compilerProc, inline.} = doIncRef(usrToCell(p))
proc nimGCunref(p: pointer) {.compilerProc, inline.} = doDecRef(usrToCell(p))
@@ -449,7 +449,7 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
doAsgnRef(dest, src, LocalHeap, MaybeCyclic)
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
# the code generator calls this proc if it is known at compile time that no
# the code generator calls this proc if it is known at compile time that no
# cycle is possible.
doAsgnRef(dest, src, LocalHeap, Acyclic)
@@ -509,7 +509,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) =
if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
else:
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
n.sons[i].typ, op)
else:
forAllSlotsAux(dest, n.sons[i], op)
@@ -557,7 +557,7 @@ proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down.
#
#
# Slots to try cache hit
# 1 32%
# 4 59%
@@ -602,7 +602,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer
acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin")
sysAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch)
sysAssert(allocInv(gch.region), "rawNewObj after collect")
@@ -610,16 +610,16 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer
sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
res.typ = typ
when trackAllocationSource and not hasThreadSupport:
if framePtr != nil and framePtr.prev != nil and framePtr.prev.prev != nil:
res.filename = framePtr.prev.prev.filename
res.line = framePtr.prev.prev.line
else:
res.filename = "nofile"
if rc1:
res.refcount = rcIncrement # refcount is 1
else:
@@ -631,9 +631,9 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer
res.setBit(rcInCycleRoots)
res.setColor rcCycleCandidate
gch.cycleRoots.add res
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
when logGC: writeCell("new cell", res)
gcTrace(res, csAllocated)
release(gch)
@@ -711,9 +711,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = if ol.typ.kind != tyString: ol.typ.base.size
else: 1
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
# XXX: This should happen outside
# call user-defined move code
# call user-defined default constructor
@@ -723,24 +723,24 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
when false:
if ol.isBitUp(rcZct):
var j = gch.zct.len-1
var d = gch.zct.d
while j >= 0:
while j >= 0:
if d[j] == ol:
d[j] = res
break
dec(j)
if ol.isBitUp(rcInCycleRoots):
for i in 0 .. <gch.cycleRoots.len:
if gch.cycleRoots.d[i] == ol:
eraseAt(gch.cycleRoots, i)
freeCell(gch, ol)
else:
# the new buffer inherits the GC state of the old one
if res.isBitUp(rcZct): gch.zct.add res
@@ -787,12 +787,12 @@ proc doOperation(p: pointer, op: WalkOp) =
var c: PCell = usrToCell(p)
sysAssert(c != nil, "doOperation: 1")
gch.tempStack.add c
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, WalkOp(op))
type
RecursionType = enum
RecursionType = enum
FromChildren,
FromRoot
{.deprecated: [TRecursionType: RecursionType].}
@@ -838,14 +838,14 @@ proc collectCycles(gch: var GcHeap) =
let startLen = gch.tempStack.len
cell.setColor rcAlive
cell.forAllChildren waPush
while startLen != gch.tempStack.len:
dec gch.tempStack.len
var c = gch.tempStack.d[gch.tempStack.len]
if c.color != rcAlive:
c.setColor rcAlive
c.forAllChildren waPush
template earlyMarkAlive(stackRoots) =
# This marks all objects reachable from the stack as alive before any
# of the other stages is executed. Such objects cannot be garbage and
@@ -856,7 +856,7 @@ proc collectCycles(gch: var GcHeap) =
earlyMarkAliveRec(c)
earlyMarkAlive(gch.decStack)
when CollectCyclesStats:
let tAfterEarlyMarkAlive = getTicks()
@@ -864,7 +864,7 @@ proc collectCycles(gch: var GcHeap) =
let startLen = gch.tempStack.len
cell.setColor rcDecRefApplied
cell.forAllChildren waPush
while startLen != gch.tempStack.len:
dec gch.tempStack.len
var c = gch.tempStack.d[gch.tempStack.len]
@@ -876,7 +876,7 @@ proc collectCycles(gch: var GcHeap) =
if c.color != rcDecRefApplied:
c.setColor rcDecRefApplied
c.forAllChildren waPush
template markRoots(roots) =
var i = 0
while i < roots.len:
@@ -885,34 +885,34 @@ proc collectCycles(gch: var GcHeap) =
inc i
else:
roots.trimAt i
markRoots(gch.cycleRoots)
when CollectCyclesStats:
let tAfterMark = getTicks()
c_printf "COLLECT CYCLES %d: %d/%d\n", gcCollectionIdx, gch.cycleRoots.len, l0
template recursiveMarkAlive(cell) =
let startLen = gch.tempStack.len
cell.setColor rcAlive
cell.forAllChildren waPush
while startLen != gch.tempStack.len:
dec gch.tempStack.len
var c = gch.tempStack.d[gch.tempStack.len]
if ignoreObject(c): continue
inc c.refcount, rcIncrement
inc increfs
if c.color != rcAlive:
c.setColor rcAlive
c.forAllChildren waPush
template scanRoots(roots) =
for i in 0 .. <roots.len:
let startLen = gch.tempStack.len
gch.tempStack.add roots.d[i]
while startLen != gch.tempStack.len:
dec gch.tempStack.len
var c = gch.tempStack.d[gch.tempStack.len]
@@ -928,9 +928,9 @@ proc collectCycles(gch: var GcHeap) =
c.setColor rcMaybeDead
inc maybedeads
c.forAllChildren waPush
scanRoots(gch.cycleRoots)
when CollectCyclesStats:
let tAfterScan = getTicks()
@@ -941,7 +941,7 @@ proc collectCycles(gch: var GcHeap) =
let startLen = gch.tempStack.len
gch.tempStack.add c
while startLen != gch.tempStack.len:
dec gch.tempStack.len
var c = gch.tempStack.d[gch.tempStack.len]
@@ -965,7 +965,7 @@ proc collectCycles(gch: var GcHeap) =
freeCell(gch, gch.freeStack.d[i])
collectDead(gch.cycleRoots)
when CollectCyclesStats:
let tFinal = getTicks()
cprintf "times:\n early mark alive: %d ms\n mark: %d ms\n scan: %d ms\n collect: %d ms\n decrefs: %d\n increfs: %d\n marked dead: %d\n collected: %d\n",
@@ -986,7 +986,7 @@ proc collectCycles(gch: var GcHeap) =
when MarkingSkipsAcyclicObjects:
# Collect the acyclic objects that became unreachable due to collected
# cyclic objects.
# cyclic objects.
discard collectZCT(gch)
# collectZCT may add new cycle candidates and we may decide to loop here
# if gch.cycleRoots.len > 0: repeat
@@ -1030,12 +1030,12 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var GcHeap) =
proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".}
var it = threadList
while it != nil:
# mark registers:
# mark registers:
for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
var sp = cast[ByteAddress](it.stackBottom)
var max = cast[ByteAddress](it.stackTop)
@@ -1121,7 +1121,7 @@ elif stackIncreases:
var b = cast[ByteAddress](stackTop)
var x = cast[ByteAddress](p)
result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
var registers: C_JmpBuf
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
@@ -1156,7 +1156,7 @@ else:
# mark the registers
var jmpbufPtr = cast[ByteAddress](addr(registers))
var jmpbufEnd = jmpbufPtr +% jmpbufSize
while jmpbufPtr <=% jmpbufEnd:
gcMark(gch, cast[PPointer](jmpbufPtr)[])
jmpbufPtr = jmpbufPtr +% sizeof(pointer)
@@ -1218,18 +1218,18 @@ proc releaseCell(gch: var GcHeap, cell: PCell) =
proc collectZCT(gch: var GcHeap): bool =
const workPackage = 100
var L = addr(gch.zct.len)
when withRealtime:
var steps = workPackage
var t0: Ticks
if gch.maxPause > 0: t0 = getticks()
while L[] > 0:
var c = gch.zct.d[0]
sysAssert c.isBitUp(rcZct), "collectZCT: rcZct missing!"
sysAssert(isAllocatedPtr(gch.region, c), "collectZCT: isAllocatedPtr")
# remove from ZCT:
# remove from ZCT:
c.clearBit(rcZct)
gch.zct.d[0] = gch.zct.d[L[] - 1]
dec(L[])
@@ -1237,7 +1237,7 @@ proc collectZCT(gch: var GcHeap): bool =
if c.refcount <% rcIncrement:
# It may have a RC > 0, if it is in the hardware stack or
# it has not been removed yet from the ZCT. This is because
# ``incref`` does not bother to remove the cell from the ZCT
# ``incref`` does not bother to remove the cell from the ZCT
# as this might be too slow.
# In any case, it should be removed from the ZCT. But not
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
@@ -1252,7 +1252,7 @@ proc collectZCT(gch: var GcHeap): bool =
steps = workPackage
if gch.maxPause > 0:
let duration = getticks() - t0
# the GC's measuring is not accurate and needs some cleanup actions
# the GC's measuring is not accurate and needs some cleanup actions
# (stack unmarking), so subtract some short amount of time in to
# order to miss deadlines less often:
if duration >= gch.maxPause - 50_000:
@@ -1269,7 +1269,7 @@ proc unmarkStackAndRegisters(gch: var GcHeap) =
# XXX: just call doDecRef?
var c = d[i]
sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
if c.color == rcRetiredBuffer:
continue
@@ -1278,7 +1278,7 @@ proc unmarkStackAndRegisters(gch: var GcHeap) =
# the object survived only because of a stack reference
# it still doesn't have heap references
addZCT(gch.zct, c)
if canbeCycleRoot(c):
# any cyclic object reachable from the stack can be turned into
# a leak if it's orphaned through the stack reference
@@ -1293,7 +1293,7 @@ proc collectCTBody(gch: var GcHeap) =
let t0 = getticks()
when debugGC: inc gcCollectionIdx
sysAssert(allocInv(gch.region), "collectCT: begin")
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
sysAssert(gch.decStack.len == 0, "collectCT")
prepareForInteriorPointerChecking(gch.region)
@@ -1312,7 +1312,7 @@ proc collectCTBody(gch: var GcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
unmarkStackAndRegisters(gch)
sysAssert(allocInv(gch.region), "collectCT: end")
when withRealtime:
let duration = getticks() - t0
gch.stat.maxPause = max(gch.stat.maxPause, duration)
@@ -1322,7 +1322,7 @@ proc collectCTBody(gch: var GcHeap) =
proc collectCT(gch: var GcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0:
collectCTBody(gch)
@@ -1337,7 +1337,7 @@ when withRealtime:
acquire(gch)
gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
strongAdvice:
collectCTBody(gch)
release(gch)
@@ -1345,13 +1345,13 @@ when withRealtime:
proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice)
when not defined(useNimRtl):
proc GC_disable() =
proc GC_disable() =
when hasThreadSupport and hasSharedHeap:
discard atomicInc(gch.recGcLock, 1)
else:
inc(gch.recGcLock)
proc GC_enable() =
if gch.recGcLock > 0:
if gch.recGcLock > 0:
when hasThreadSupport and hasSharedHeap:
discard atomicDec(gch.recGcLock, 1)
else:

View File

@@ -197,7 +197,7 @@ else:
var x = cast[ByteAddress](p)
if a <=% x and x <=% b:
return true
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
# We use a jmp_buf buffer that is in the C stack.
# Used to traverse the stack and registers assuming
@@ -207,7 +207,7 @@ else:
getRegisters(registers)
for i in registers.low .. registers.high:
gcMark(gch, cast[PPointer](registers[i]))
for stack in items(gch.stack):
stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts))
var max = cast[ByteAddress](stack.starts)

View File

@@ -20,21 +20,21 @@ when not defined(nimNewShared):
{.pragma: gcsafe.}
when defined(createNimRtl):
when defined(useNimRtl):
when defined(useNimRtl):
{.error: "Cannot create and use nimrtl at the same time!".}
elif appType != "lib":
{.error: "nimrtl must be built as a library!".}
when defined(createNimRtl):
when defined(createNimRtl):
{.pragma: rtl, exportc: "nimrtl_$1", dynlib, gcsafe.}
{.pragma: inl.}
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
elif defined(useNimRtl):
when defined(windows):
when defined(windows):
const nimrtl* = "nimrtl.dll"
elif defined(macosx):
const nimrtl* = "libnimrtl.dylib"
else:
else:
const nimrtl* = "libnimrtl.so"
{.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.}
{.pragma: inl.}

View File

@@ -40,7 +40,7 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
while it != nil:
inc(total)
it = it.prev
for j in 1..total-i-(firstCalls-1):
for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev
if total != i:
st[i] = "..."

View File

@@ -23,7 +23,7 @@ when defined(Windows):
SysCond = Handle
{.deprecated: [THandle: Handle, TSysLock: SysLock, TSysCond: SysCond].}
proc initSysLock(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "InitializeCriticalSection".}
## Initializes the lock `L`.
@@ -31,14 +31,14 @@ when defined(Windows):
proc tryAcquireSysAux(L: var SysLock): int32 {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "TryEnterCriticalSection".}
## Tries to acquire the lock `L`.
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) != 0'i32
proc acquireSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "EnterCriticalSection".}
## Acquires the lock `L`.
proc releaseSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "LeaveCriticalSection".}
## Releases the lock `L`.
@@ -46,11 +46,11 @@ when defined(Windows):
proc deinitSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "DeleteCriticalSection".}
proc createEvent(lpEventAttributes: pointer,
proc createEvent(lpEventAttributes: pointer,
bManualReset, bInitialState: int32,
lpName: cstring): SysCond {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "CreateEventA".}
proc closeHandle(hObject: Handle) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "CloseHandle".}
proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {.
@@ -58,7 +58,7 @@ when defined(Windows):
proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "SetEvent".}
proc initSysCond(cond: var SysCond) {.inline.} =
cond = createEvent(nil, 0'i32, 0'i32, nil)
proc deinitSysCond(cond: var SysCond) {.inline.} =
@@ -86,7 +86,7 @@ else:
proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) == 0'i32
proc releaseSys(L: var SysLock) {.noSideEffect,
@@ -100,7 +100,7 @@ else:
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
proc signalSysCond(cond: var SysCond) {.
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
proc deinitSysCond(cond: var SysCond) {.noSideEffect,
importc: "pthread_cond_destroy", header: "<pthread.h>".}

View File

@@ -9,7 +9,7 @@
## Implements Nim's 'spawn'.
when not declared(NimString):
when not declared(NimString):
{.error: "You must not import this module explicitly".}
{.push stackTrace:off.}

View File

@@ -34,8 +34,8 @@ when defined(windows):
elif defined(macosx):
type
MachTimebaseInfoData {.pure, final,
importc: "mach_timebase_info_data_t",
MachTimebaseInfoData {.pure, final,
importc: "mach_timebase_info_data_t",
header: "<mach/mach_time.h>".} = object
numer, denom: int32
{.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
@@ -46,10 +46,10 @@ elif defined(macosx):
proc getTicks(): Ticks {.inline.} =
result = Ticks(mach_absolute_time())
var timeBaseInfo: MachTimebaseInfoData
mach_timebase_info(timeBaseInfo)
proc `-`(a, b: Ticks): Nanos =
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
@@ -57,10 +57,10 @@ elif defined(posixRealtime):
type
Clockid {.importc: "clockid_t", header: "<time.h>", final.} = object
TimeSpec {.importc: "struct timespec", header: "<time.h>",
TimeSpec {.importc: "struct timespec", header: "<time.h>",
final, pure.} = object ## struct timespec
tv_sec: int ## Seconds.
tv_nsec: int ## Nanoseconds.
tv_sec: int ## Seconds.
tv_nsec: int ## Nanoseconds.
{.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
var
@@ -77,12 +77,12 @@ elif defined(posixRealtime):
proc `-`(a, b: Ticks): Nanos {.borrow.}
else:
# fallback Posix implementation:
# fallback Posix implementation:
type
Timeval {.importc: "struct timeval", header: "<sys/select.h>",
Timeval {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec: int ## Seconds.
tv_usec: int ## Microseconds.
tv_sec: int ## Seconds.
tv_usec: int ## Microseconds.
{.deprecated: [Ttimeval: Timeval].}
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>".}
@@ -90,7 +90,7 @@ else:
proc getTicks(): Ticks =
var t: Timeval
posix_gettimeofday(t)
result = Ticks(int64(t.tv_sec) * 1000_000_000'i64 +
result = Ticks(int64(t.tv_sec) * 1000_000_000'i64 +
int64(t.tv_usec) * 1000'i64)
proc `-`(a, b: Ticks): Nanos {.borrow.}

View File

@@ -124,7 +124,7 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
# If the 16 bits following the high surrogate are in the source buffer...
let ch2 = int(cast[uint16](w[i]))
# If it's a low surrogate, convert to UTF32:
if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
@@ -135,7 +135,7 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
#invalid UTF-16
ch = replacement
if ch < 0x80:
result.add chr(ch)
elif ch < 0x800:
@@ -155,6 +155,6 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
result.add chr(0xFFFD shr 12 or 0b1110_0000)
result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
proc `$`*(s: WideCString): string =
result = s $ 80

View File

@@ -10,9 +10,9 @@
## This module is a low level wrapper for `libsvm`:idx:.
{.deadCodeElim: on.}
const
const
LIBSVM_VERSION* = 312
when defined(windows):
const svmdll* = "libsvm.dll"
elif defined(macosx):
@@ -20,97 +20,97 @@ elif defined(macosx):
else:
const svmdll* = "libsvm.so"
type
Node*{.pure, final.} = object
type
Node*{.pure, final.} = object
index*: cint
value*: cdouble
Problem*{.pure, final.} = object
Problem*{.pure, final.} = object
L*: cint
y*: ptr cdouble
x*: ptr ptr Node
Type*{.size: sizeof(cint).} = enum
Type*{.size: sizeof(cint).} = enum
C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR
KernelType*{.size: sizeof(cint).} = enum
KernelType*{.size: sizeof(cint).} = enum
LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED
Parameter*{.pure, final.} = object
Parameter*{.pure, final.} = object
typ*: Type
kernelType*: KernelType
degree*: cint # for poly
gamma*: cdouble # for poly/rbf/sigmoid
coef0*: cdouble # for poly/sigmoid
# these are for training only
cache_size*: cdouble # in MB
eps*: cdouble # stopping criteria
C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR
nr_weight*: cint # for C_SVC
weight_label*: ptr cint # for C_SVC
weight*: ptr cdouble # for C_SVC
nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR
p*: cdouble # for EPSILON_SVR
shrinking*: cint # use the shrinking heuristics
probability*: cint # do probability estimates
degree*: cint # for poly
gamma*: cdouble # for poly/rbf/sigmoid
coef0*: cdouble # for poly/sigmoid
# these are for training only
cache_size*: cdouble # in MB
eps*: cdouble # stopping criteria
C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR
nr_weight*: cint # for C_SVC
weight_label*: ptr cint # for C_SVC
weight*: ptr cdouble # for C_SVC
nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR
p*: cdouble # for EPSILON_SVR
shrinking*: cint # use the shrinking heuristics
probability*: cint # do probability estimates
{.deprecated: [Tnode: Node, Tproblem: Problem, Ttype: Type,
TKernelType: KernelType, Tparameter: Parameter].}
#
# svm_model
#
#
type
Model*{.pure, final.} = object
param*: Parameter # parameter
nr_class*: cint # number of classes, = 2 in regression/one class svm
L*: cint # total #SV
type
Model*{.pure, final.} = object
param*: Parameter # parameter
nr_class*: cint # number of classes, = 2 in regression/one class svm
L*: cint # total #SV
SV*: ptr ptr Node # SVs (SV[l])
sv_coef*: ptr ptr cdouble # coefficients for SVs in decision functions (sv_coef[k-1][l])
rho*: ptr cdouble # constants in decision functions (rho[k*(k-1)/2])
probA*: ptr cdouble # pariwise probability information
probB*: ptr cdouble # for classification only
label*: ptr cint # label of each class (label[k])
nSV*: ptr cint # number of SVs for each class (nSV[k])
# nSV[0] + nSV[1] + ... + nSV[k-1] = l
# XXX
sv_coef*: ptr ptr cdouble # coefficients for SVs in decision functions (sv_coef[k-1][l])
rho*: ptr cdouble # constants in decision functions (rho[k*(k-1)/2])
probA*: ptr cdouble # pariwise probability information
probB*: ptr cdouble # for classification only
label*: ptr cint # label of each class (label[k])
nSV*: ptr cint # number of SVs for each class (nSV[k])
# nSV[0] + nSV[1] + ... + nSV[k-1] = l
# XXX
free_sv*: cint # 1 if svm_model is created by svm_load_model
# 0 if svm_model is created by svm_train
{.deprecated: [TModel: Model].}
proc train*(prob: ptr Problem, param: ptr Parameter): ptr Model{.cdecl,
proc train*(prob: ptr Problem, param: ptr Parameter): ptr Model{.cdecl,
importc: "svm_train", dynlib: svmdll.}
proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint,
target: ptr cdouble){.cdecl,
proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint,
target: ptr cdouble){.cdecl,
importc: "svm_cross_validation", dynlib: svmdll.}
proc save_model*(model_file_name: cstring, model: ptr Model): cint{.cdecl,
proc save_model*(model_file_name: cstring, model: ptr Model): cint{.cdecl,
importc: "svm_save_model", dynlib: svmdll.}
proc load_model*(model_file_name: cstring): ptr Model{.cdecl,
proc load_model*(model_file_name: cstring): ptr Model{.cdecl,
importc: "svm_load_model", dynlib: svmdll.}
proc get_svm_type*(model: ptr Model): cint{.cdecl, importc: "svm_get_svm_type",
proc get_svm_type*(model: ptr Model): cint{.cdecl, importc: "svm_get_svm_type",
dynlib: svmdll.}
proc get_nr_class*(model: ptr Model): cint{.cdecl, importc: "svm_get_nr_class",
proc get_nr_class*(model: ptr Model): cint{.cdecl, importc: "svm_get_nr_class",
dynlib: svmdll.}
proc get_labels*(model: ptr Model, label: ptr cint){.cdecl,
proc get_labels*(model: ptr Model, label: ptr cint){.cdecl,
importc: "svm_get_labels", dynlib: svmdll.}
proc get_svr_probability*(model: ptr Model): cdouble{.cdecl,
proc get_svr_probability*(model: ptr Model): cdouble{.cdecl,
importc: "svm_get_svr_probability", dynlib: svmdll.}
proc predict_values*(model: ptr Model, x: ptr Node, dec_values: ptr cdouble): cdouble{.
cdecl, importc: "svm_predict_values", dynlib: svmdll.}
proc predict*(model: ptr Model, x: ptr Node): cdouble{.cdecl,
proc predict*(model: ptr Model, x: ptr Node): cdouble{.cdecl,
importc: "svm_predict", dynlib: svmdll.}
proc predict_probability*(model: ptr Model, x: ptr Node,
prob_estimates: ptr cdouble): cdouble{.cdecl,
proc predict_probability*(model: ptr Model, x: ptr Node,
prob_estimates: ptr cdouble): cdouble{.cdecl,
importc: "svm_predict_probability", dynlib: svmdll.}
proc free_model_content*(model_ptr: ptr Model){.cdecl,
proc free_model_content*(model_ptr: ptr Model){.cdecl,
importc: "svm_free_model_content", dynlib: svmdll.}
proc free_and_destroy_model*(model_ptr_ptr: ptr ptr Model){.cdecl,
proc free_and_destroy_model*(model_ptr_ptr: ptr ptr Model){.cdecl,
importc: "svm_free_and_destroy_model", dynlib: svmdll.}
proc destroy_param*(param: ptr Parameter){.cdecl, importc: "svm_destroy_param",
proc destroy_param*(param: ptr Parameter){.cdecl, importc: "svm_destroy_param",
dynlib: svmdll.}
proc check_parameter*(prob: ptr Problem, param: ptr Parameter): cstring{.
cdecl, importc: "svm_check_parameter", dynlib: svmdll.}
proc check_probability_model*(model: ptr Model): cint{.cdecl,
proc check_probability_model*(model: ptr Model): cint{.cdecl,
importc: "svm_check_probability_model", dynlib: svmdll.}
proc set_print_string_function*(print_func: proc (arg: cstring) {.cdecl.}){.

File diff suppressed because it is too large Load Diff

View File

@@ -8,17 +8,17 @@
#
{.deadCodeElim: on.}
when defined(windows):
const
when defined(windows):
const
Lib = "sqlite3.dll"
elif defined(macosx):
const
elif defined(macosx):
const
Lib = "libsqlite3(|.0).dylib"
else:
const
else:
const
Lib = "libsqlite3.so(|.0)"
const
const
SQLITE_INTEGER* = 1
SQLITE_FLOAT* = 2
SQLITE_BLOB* = 4
@@ -26,38 +26,38 @@ const
SQLITE_TEXT* = 3
SQLITE_UTF8* = 1
SQLITE_UTF16LE* = 2
SQLITE_UTF16BE* = 3 # Use native byte order
SQLITE_UTF16* = 4 # sqlite3_create_function only
SQLITE_UTF16BE* = 3 # Use native byte order
SQLITE_UTF16* = 4 # sqlite3_create_function only
SQLITE_ANY* = 5 #sqlite_exec return values
SQLITE_OK* = 0
SQLITE_ERROR* = 1 # SQL error or missing database
SQLITE_INTERNAL* = 2 # An internal logic error in SQLite
SQLITE_PERM* = 3 # Access permission denied
SQLITE_ABORT* = 4 # Callback routine requested an abort
SQLITE_BUSY* = 5 # The database file is locked
SQLITE_LOCKED* = 6 # A table in the database is locked
SQLITE_NOMEM* = 7 # A malloc() failed
SQLITE_READONLY* = 8 # Attempt to write a readonly database
SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt()
SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred
SQLITE_CORRUPT* = 11 # The database disk image is malformed
SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found
SQLITE_FULL* = 13 # Insertion failed because database is full
SQLITE_CANTOPEN* = 14 # Unable to open the database file
SQLITE_PROTOCOL* = 15 # Database lock protocol error
SQLITE_EMPTY* = 16 # Database is empty
SQLITE_SCHEMA* = 17 # The database schema changed
SQLITE_TOOBIG* = 18 # Too much data for one row of a table
SQLITE_CONSTRAINT* = 19 # Abort due to contraint violation
SQLITE_MISMATCH* = 20 # Data type mismatch
SQLITE_MISUSE* = 21 # Library used incorrectly
SQLITE_NOLFS* = 22 # Uses OS features not supported on host
SQLITE_AUTH* = 23 # Authorization denied
SQLITE_FORMAT* = 24 # Auxiliary database format error
SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range
SQLITE_NOTADB* = 26 # File opened that is not a database file
SQLITE_ROW* = 100 # sqlite3_step() has another row ready
SQLITE_DONE* = 101 # sqlite3_step() has finished executing
SQLITE_ERROR* = 1 # SQL error or missing database
SQLITE_INTERNAL* = 2 # An internal logic error in SQLite
SQLITE_PERM* = 3 # Access permission denied
SQLITE_ABORT* = 4 # Callback routine requested an abort
SQLITE_BUSY* = 5 # The database file is locked
SQLITE_LOCKED* = 6 # A table in the database is locked
SQLITE_NOMEM* = 7 # A malloc() failed
SQLITE_READONLY* = 8 # Attempt to write a readonly database
SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt()
SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred
SQLITE_CORRUPT* = 11 # The database disk image is malformed
SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found
SQLITE_FULL* = 13 # Insertion failed because database is full
SQLITE_CANTOPEN* = 14 # Unable to open the database file
SQLITE_PROTOCOL* = 15 # Database lock protocol error
SQLITE_EMPTY* = 16 # Database is empty
SQLITE_SCHEMA* = 17 # The database schema changed
SQLITE_TOOBIG* = 18 # Too much data for one row of a table
SQLITE_CONSTRAINT* = 19 # Abort due to contraint violation
SQLITE_MISMATCH* = 20 # Data type mismatch
SQLITE_MISUSE* = 21 # Library used incorrectly
SQLITE_NOLFS* = 22 # Uses OS features not supported on host
SQLITE_AUTH* = 23 # Authorization denied
SQLITE_FORMAT* = 24 # Auxiliary database format error
SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range
SQLITE_NOTADB* = 26 # File opened that is not a database file
SQLITE_ROW* = 100 # sqlite3_step() has another row ready
SQLITE_DONE* = 101 # sqlite3_step() has finished executing
SQLITE_COPY* = 0
SQLITE_CREATE_INDEX* = 1
SQLITE_CREATE_TABLE* = 2
@@ -87,39 +87,39 @@ const
SQLITE_ALTER_TABLE* = 26
SQLITE_REINDEX* = 27
SQLITE_DENY* = 1
SQLITE_IGNORE* = 2 # Original from sqlite3.h:
SQLITE_IGNORE* = 2 # Original from sqlite3.h:
#define SQLITE_STATIC ((void(*)(void *))0)
#define SQLITE_TRANSIENT ((void(*)(void *))-1)
SQLITE_DETERMINISTIC* = 0x800
const
const
SQLITE_STATIC* = nil
SQLITE_TRANSIENT* = cast[pointer](- 1)
type
Sqlite3 {.pure, final.} = object
type
Sqlite3 {.pure, final.} = object
PSqlite3* = ptr Sqlite3
PPSqlite3* = ptr PSqlite3
Context{.pure, final.} = object
Context{.pure, final.} = object
Pcontext* = ptr Context
Tstmt{.pure, final.} = object
Tstmt{.pure, final.} = object
Pstmt* = ptr Tstmt
Value{.pure, final.} = object
Value{.pure, final.} = object
Pvalue* = ptr Value
PValueArg* = array[0..127, Pvalue]
Callback* = proc (para1: pointer, para2: int32, para3,
Callback* = proc (para1: pointer, para2: int32, para3,
para4: cstringArray): int32{.cdecl.}
Tbind_destructor_func* = proc (para1: pointer){.cdecl.}
Create_function_step_func* = proc (para1: Pcontext, para2: int32,
Create_function_step_func* = proc (para1: Pcontext, para2: int32,
para3: PValueArg){.cdecl.}
Create_function_func_func* = proc (para1: Pcontext, para2: int32,
Create_function_func_func* = proc (para1: Pcontext, para2: int32,
para3: PValueArg){.cdecl.}
Create_function_final_func* = proc (para1: Pcontext){.cdecl.}
Result_func* = proc (para1: pointer){.cdecl.}
Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer,
Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer,
para4: int32, para5: pointer): int32{.cdecl.}
Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32,
Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32,
para4: cstring){.cdecl.}
{.deprecated: [TSqlite3: Sqlite3, TContext: Context, Tvalue: Value,
Tcallback: Callback, Tcreate_function_step_func: Create_function_step_func,
@@ -129,220 +129,220 @@ type
Tcollation_needed_func: Collation_needed_func].}
proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".}
proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
errmsg: var cstring): int32{.cdecl, dynlib: Lib,
proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
errmsg: var cstring): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_exec".}
proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, dynlib: Lib,
proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, dynlib: Lib,
importc: "sqlite3_last_insert_rowid".}
proc changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_changes".}
proc total_changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib,
proc total_changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_total_changes".}
proc interrupt*(para1: PSqlite3){.cdecl, dynlib: Lib, importc: "sqlite3_interrupt".}
proc complete*(sql: cstring): int32{.cdecl, dynlib: Lib,
proc complete*(sql: cstring): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_complete".}
proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib,
proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_complete16".}
proc busy_handler*(para1: PSqlite3,
para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
para3: pointer): int32{.cdecl, dynlib: Lib,
proc busy_handler*(para1: PSqlite3,
para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
para3: pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_busy_handler".}
proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, dynlib: Lib,
proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_busy_timeout".}
proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_get_table".}
proc free_table*(result: cstringArray){.cdecl, dynlib: Lib,
proc free_table*(result: cstringArray){.cdecl, dynlib: Lib,
importc: "sqlite3_free_table".}
# Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf
# function sqlite3_mprintf(_para1:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_mprintf';
proc mprintf*(para1: cstring): cstring{.cdecl, varargs, dynlib: Lib,
proc mprintf*(para1: cstring): cstring{.cdecl, varargs, dynlib: Lib,
importc: "sqlite3_mprintf".}
#function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf';
proc free*(z: cstring){.cdecl, dynlib: Lib, importc: "sqlite3_free".}
#function sqlite3_snprintf(_para1:longint; _para2:Pchar; _para3:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_snprintf';
proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl,
proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl,
dynlib: Lib, varargs, importc: "sqlite3_snprintf".}
proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32,
proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32,
para3: cstring, para4: cstring, para5: cstring, para6: cstring): int32{.
cdecl.}, pUserData: pointer): int32{.cdecl, dynlib: Lib,
cdecl.}, pUserData: pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_set_authorizer".}
proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
para3: pointer): pointer{.cdecl, dynlib: Lib,
proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
para3: pointer): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_trace".}
proc progress_handler*(para1: PSqlite3, para2: int32,
para3: proc (para1: pointer): int32{.cdecl.},
para4: pointer){.cdecl, dynlib: Lib,
proc progress_handler*(para1: PSqlite3, para2: int32,
para3: proc (para1: pointer): int32{.cdecl.},
para4: pointer){.cdecl, dynlib: Lib,
importc: "sqlite3_progress_handler".}
proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
para3: pointer): pointer{.cdecl, dynlib: Lib,
proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
para3: pointer): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_commit_hook".}
proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_open".}
proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_open16".}
proc errcode*(db: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_errcode".}
proc errmsg*(para1: PSqlite3): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_errmsg".}
proc errmsg16*(para1: PSqlite3): pointer{.cdecl, dynlib: Lib,
proc errmsg16*(para1: PSqlite3): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_errmsg16".}
proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt,
pzTail: ptr cstring): int32{.cdecl, dynlib: Lib,
proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt,
pzTail: ptr cstring): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_prepare".}
proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var Pstmt,
pzTail: ptr cstring): cint {.
importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.}
proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt,
pzTail: var pointer): int32{.cdecl, dynlib: Lib,
proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt,
pzTail: var pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_prepare16".}
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_blob".}
proc bind_double*(para1: Pstmt, para2: int32, para3: float64): int32{.cdecl,
proc bind_double*(para1: Pstmt, para2: int32, para3: float64): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_double".}
proc bind_int*(para1: Pstmt, para2: int32, para3: int32): int32{.cdecl,
proc bind_int*(para1: Pstmt, para2: int32, para3: int32): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_int".}
proc bind_int64*(para1: Pstmt, para2: int32, para3: int64): int32{.cdecl,
proc bind_int64*(para1: Pstmt, para2: int32, para3: int64): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_int64".}
proc bind_null*(para1: Pstmt, para2: int32): int32{.cdecl, dynlib: Lib,
proc bind_null*(para1: Pstmt, para2: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_null".}
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text".}
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text16".}
#function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value';
#These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT
#It's the c world man ;-)
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
para5: int32): int32{.cdecl, dynlib: Lib,
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
para5: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_blob".}
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
para5: int32): int32{.cdecl, dynlib: Lib,
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
para5: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text".}
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
para5: int32): int32{.cdecl, dynlib: Lib,
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
para5: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text16".}
proc bind_parameter_count*(para1: Pstmt): int32{.cdecl, dynlib: Lib,
proc bind_parameter_count*(para1: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_parameter_count".}
proc bind_parameter_name*(para1: Pstmt, para2: int32): cstring{.cdecl,
proc bind_parameter_name*(para1: Pstmt, para2: int32): cstring{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_parameter_name".}
proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl,
proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_parameter_index".}
#function sqlite3_clear_bindings(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_clear_bindings';
proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_count".}
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
importc: "sqlite3_column_name".}
proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_column_name16".}
proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,
proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,
importc: "sqlite3_column_decltype".}
proc column_decltype16*(para1: Pstmt, para2: int32): pointer{.cdecl,
proc column_decltype16*(para1: Pstmt, para2: int32): pointer{.cdecl,
dynlib: Lib, importc: "sqlite3_column_decltype16".}
proc step*(para1: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_step".}
proc data_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
proc data_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_data_count".}
proc column_blob*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
proc column_blob*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_column_blob".}
proc column_bytes*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
proc column_bytes*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_bytes".}
proc column_bytes16*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
proc column_bytes16*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_bytes16".}
proc column_double*(para1: Pstmt, iCol: int32): float64{.cdecl, dynlib: Lib,
proc column_double*(para1: Pstmt, iCol: int32): float64{.cdecl, dynlib: Lib,
importc: "sqlite3_column_double".}
proc column_int*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
proc column_int*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_int".}
proc column_int64*(para1: Pstmt, iCol: int32): int64{.cdecl, dynlib: Lib,
proc column_int64*(para1: Pstmt, iCol: int32): int64{.cdecl, dynlib: Lib,
importc: "sqlite3_column_int64".}
proc column_text*(para1: Pstmt, iCol: int32): cstring{.cdecl, dynlib: Lib,
proc column_text*(para1: Pstmt, iCol: int32): cstring{.cdecl, dynlib: Lib,
importc: "sqlite3_column_text".}
proc column_text16*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
proc column_text16*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_column_text16".}
proc column_type*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
proc column_type*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_type".}
proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_finalize".}
proc reset*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_reset".}
proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
eTextRep: int32, para5: pointer,
xFunc: Create_function_func_func,
xStep: Create_function_step_func,
xFinal: Create_function_final_func): int32{.cdecl,
proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
eTextRep: int32, para5: pointer,
xFunc: Create_function_func_func,
xStep: Create_function_step_func,
xFinal: Create_function_final_func): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_create_function".}
proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
eTextRep: int32, para5: pointer,
xFunc: Create_function_func_func,
xStep: Create_function_step_func,
xFinal: Create_function_final_func): int32{.cdecl,
proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
eTextRep: int32, para5: pointer,
xFunc: Create_function_func_func,
xStep: Create_function_step_func,
xFinal: Create_function_final_func): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_create_function16".}
proc aggregate_count*(para1: Pcontext): int32{.cdecl, dynlib: Lib,
proc aggregate_count*(para1: Pcontext): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_aggregate_count".}
proc value_blob*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
proc value_blob*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_value_blob".}
proc value_bytes*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
proc value_bytes*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_value_bytes".}
proc value_bytes16*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
proc value_bytes16*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_value_bytes16".}
proc value_double*(para1: Pvalue): float64{.cdecl, dynlib: Lib,
proc value_double*(para1: Pvalue): float64{.cdecl, dynlib: Lib,
importc: "sqlite3_value_double".}
proc value_int*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
proc value_int*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_value_int".}
proc value_int64*(para1: Pvalue): int64{.cdecl, dynlib: Lib,
proc value_int64*(para1: Pvalue): int64{.cdecl, dynlib: Lib,
importc: "sqlite3_value_int64".}
proc value_text*(para1: Pvalue): cstring{.cdecl, dynlib: Lib,
proc value_text*(para1: Pvalue): cstring{.cdecl, dynlib: Lib,
importc: "sqlite3_value_text".}
proc value_text16*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
proc value_text16*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_value_text16".}
proc value_text16le*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
proc value_text16le*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_value_text16le".}
proc value_text16be*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
proc value_text16be*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_value_text16be".}
proc value_type*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
proc value_type*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_value_type".}
proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl,
proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl,
dynlib: Lib, importc: "sqlite3_aggregate_context".}
proc user_data*(para1: Pcontext): pointer{.cdecl, dynlib: Lib,
proc user_data*(para1: Pcontext): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_user_data".}
proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, dynlib: Lib,
proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_get_auxdata".}
proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib,
proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib,
importc: "sqlite3_set_auxdata".}
proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_blob".}
proc result_double*(para1: Pcontext, para2: float64){.cdecl, dynlib: Lib,
proc result_double*(para1: Pcontext, para2: float64){.cdecl, dynlib: Lib,
importc: "sqlite3_result_double".}
proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl,
proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl,
dynlib: Lib, importc: "sqlite3_result_error".}
proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl,
proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl,
dynlib: Lib, importc: "sqlite3_result_error16".}
proc result_int*(para1: Pcontext, para2: int32){.cdecl, dynlib: Lib,
proc result_int*(para1: Pcontext, para2: int32){.cdecl, dynlib: Lib,
importc: "sqlite3_result_int".}
proc result_int64*(para1: Pcontext, para2: int64){.cdecl, dynlib: Lib,
proc result_int64*(para1: Pcontext, para2: int64){.cdecl, dynlib: Lib,
importc: "sqlite3_result_int64".}
proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib,
proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib,
importc: "sqlite3_result_null".}
proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text".}
proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text16".}
proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text16le".}
proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text16be".}
proc result_value*(para1: Pcontext, para2: Pvalue){.cdecl, dynlib: Lib,
proc result_value*(para1: Pcontext, para2: Pvalue){.cdecl, dynlib: Lib,
importc: "sqlite3_result_value".}
proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32,
proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32,
para4: pointer, xCompare: Create_collation_func): int32{.
cdecl, dynlib: Lib, importc: "sqlite3_create_collation".}
proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32,
proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32,
para4: pointer, xCompare: Create_collation_func): int32{.
cdecl, dynlib: Lib, importc: "sqlite3_create_collation16".}
proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
@@ -350,10 +350,10 @@ proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_
proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
cdecl, dynlib: Lib, importc: "sqlite3_collation_needed16".}
proc libversion*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
#Alias for allowing better code portability (win32 is not working with external variables)
#Alias for allowing better code portability (win32 is not working with external variables)
proc version*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
# Not published functions
proc libversion_number*(): int32{.cdecl, dynlib: Lib,
proc libversion_number*(): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_libversion_number".}
#function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key';
#function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey';

View File

@@ -10,7 +10,7 @@
type
CcState {.pure, final.} = object
PccState* = ptr CcState
ErrorFunc* = proc (opaque: pointer, msg: cstring) {.cdecl.}
{.deprecated: [TccState: CcState, TErrorFunc: ErrorFunc].}
@@ -31,33 +31,33 @@ proc setWarning*(s: PccState, warningName: cstring, value: int) {.cdecl,
importc: "tcc_set_warning".}
## set/reset a warning
# preprocessor
# preprocessor
proc addIncludePath*(s: PccState, pathname: cstring) {.cdecl,
proc addIncludePath*(s: PccState, pathname: cstring) {.cdecl,
importc: "tcc_add_include_path".}
## add include path
proc addSysincludePath*(s: PccState, pathname: cstring) {.cdecl,
proc addSysincludePath*(s: PccState, pathname: cstring) {.cdecl,
importc: "tcc_add_sysinclude_path".}
## add in system include path
proc defineSymbol*(s: PccState, sym, value: cstring) {.cdecl,
proc defineSymbol*(s: PccState, sym, value: cstring) {.cdecl,
importc: "tcc_define_symbol".}
## define preprocessor symbol 'sym'. Can put optional value
proc undefineSymbol*(s: PccState, sym: cstring) {.cdecl,
proc undefineSymbol*(s: PccState, sym: cstring) {.cdecl,
importc: "tcc_undefine_symbol".}
## undefine preprocess symbol 'sym'
# compiling
# compiling
proc addFile*(s: PccState, filename: cstring): cint {.cdecl,
proc addFile*(s: PccState, filename: cstring): cint {.cdecl,
importc: "tcc_add_file".}
## add a file (either a C file, dll, an object, a library or an ld
## script). Return -1 if error.
proc compileString*(s: PccState, buf: cstring): cint {.cdecl,
proc compileString*(s: PccState, buf: cstring): cint {.cdecl,
importc: "tcc_compile_string".}
## compile a string containing a C source. Return non zero if error.
@@ -71,12 +71,12 @@ const
OutputDll*: cint = 2 ## dynamic library
OutputObj*: cint = 3 ## object file
OutputPreprocess*: cint = 4 ## preprocessed file (used internally)
OutputFormatElf*: cint = 0 ## default output format: ELF
OutputFormatBinary*: cint = 1 ## binary image output
OutputFormatCoff*: cint = 2 ## COFF
proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
importc: "tcc_set_output_type".}
## set output type. MUST BE CALLED before any compilation
@@ -115,5 +115,5 @@ proc getSymbol*(s: PccState, name: cstring): pointer {.cdecl,
proc setLibPath*(s: PccState, path: cstring) {.cdecl,
importc: "tcc_set_lib_path".}
## set CONFIG_TCCDIR at runtime