lib: Trim .nim files trailing whitespace

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

View file

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

View file

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

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # 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. ## is implemented for other databases too.
import strutils, sqlite3 import strutils, sqlite3
@ -19,31 +19,31 @@ type
InstantRow* = Pstmt ## a handle that can be used to get a row's column InstantRow* = Pstmt ## a handle that can be used to get a row's column
## text on demand ## text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].} {.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} = 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: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = SqlQuery(query) result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} = proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
new(e) new(e)
e.msg = $sqlite3.errmsg(db) e.msg = $sqlite3.errmsg(db)
raise e raise e
proc dbError*(msg: string) {.noreturn.} = proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`. ## raises an EDb exception with message `msg`.
var e: ref EDb var e: ref EDb
new(e) new(e)
@ -67,8 +67,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
inc(a) inc(a)
else: else:
add(result, c) add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery, proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} = args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args) var q = dbFormat(query, args)
@ -81,32 +81,32 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDb, FWriteDb].} = tags: [FReadDb, FWriteDb].} =
## executes the query and raises EDB if not successful. ## executes the query and raises EDB if not successful.
if not tryExec(db, query, args): dbError(db) if not tryExec(db, query, args): dbError(db)
proc newRow(L: int): Row = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery, proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt = args: varargs[string]): Pstmt =
var q = dbFormat(query, args) var q = dbFormat(query, args)
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db) if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
proc setRow(stmt: Pstmt, r: var Row, cols: cint) = proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
for col in 0..cols-1: for col in 0..cols-1:
setLen(r[col], column_bytes(stmt, col)) # set capacity setLen(r[col], column_bytes(stmt, col)) # set capacity
setLen(r[col], 0) setLen(r[col], 0)
let x = column_text(stmt, col) let x = column_text(stmt, col)
if not isNil(x): add(r[col], x) if not isNil(x): add(r[col], x)
iterator fastRows*(db: DbConn, query: SqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Sqlite it is safe though. ## query, the results can be undefined. For Sqlite it is safe though.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
var L = (column_count(stmt)) var L = (column_count(stmt))
var result = newRow(L) var result = newRow(L)
while step(stmt) == SQLITE_ROW: while step(stmt) == SQLITE_ROW:
setRow(stmt, result, L) setRow(stmt, result, L)
yield result yield result
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
@ -136,31 +136,31 @@ proc getRow*(db: DbConn, query: SqlQuery,
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
var L = (column_count(stmt)) var L = (column_count(stmt))
result = newRow(L) result = newRow(L)
if step(stmt) == SQLITE_ROW: if step(stmt) == SQLITE_ROW:
setRow(stmt, result, L) setRow(stmt, result, L)
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc getAllRows*(db: DbConn, query: SqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
## executes the query and returns the whole result dataset. ## executes the query and returns the whole result dataset.
result = @[] result = @[]
for r in fastRows(db, query, args): for r in fastRows(db, query, args):
result.add(r) result.add(r)
iterator rows*(db: DbConn, query: SqlQuery, iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## same as `FastRows`, but slower and safe. ## same as `FastRows`, but slower and safe.
for r in fastRows(db, query, args): yield r for r in fastRows(db, query, args): yield r
proc getValue*(db: DbConn, query: SqlQuery, proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDb].} = args: varargs[string, `$`]): string {.tags: [FReadDb].} =
## executes the query and returns the first column of the first row of the ## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database ## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL. ## value is NULL.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
if step(stmt) == SQLITE_ROW: if step(stmt) == SQLITE_ROW:
let cb = column_bytes(stmt, 0) let cb = column_bytes(stmt, 0)
if cb == 0: if cb == 0:
result = "" result = ""
else: else:
result = newStringOfCap(cb) result = newStringOfCap(cb)
@ -168,12 +168,12 @@ proc getValue*(db: DbConn, query: SqlQuery,
else: else:
result = "" result = ""
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: DbConn, query: SqlQuery, proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} = {.tags: [FWriteDb], raises: [].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. ## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args) var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt var stmt: sqlite3.Pstmt
result = -1 result = -1
@ -183,27 +183,27 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
if finalize(stmt) != SQLITE_OK: if finalize(stmt) != SQLITE_OK:
result = -1 result = -1
proc insertID*(db: DbConn, query: SqlQuery, proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds ## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is ## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``. ## named ``id``.
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {. args: varargs[string, `$`]): int64 {.
tags: [FReadDb, FWriteDb].} = tags: [FReadDb, FWriteDb].} =
## executes the query (typically "UPDATE") and returns the ## executes the query (typically "UPDATE") and returns the
## number of affected rows. ## number of affected rows.
exec(db, query, args) exec(db, query, args)
result = changes(db) result = changes(db)
proc close*(db: DbConn) {.tags: [FDb].} = proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection. ## closes the database connection.
if sqlite3.close(db) != SQLITE_OK: dbError(db) if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): DbConn {. proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} = tags: [FDb].} =
## opens a database connection. Raises `EDb` if the connection could not ## opens a database connection. Raises `EDb` if the connection could not
@ -216,12 +216,12 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
## ##
## Note that the encoding cannot be changed once it's been set. ## Note that the encoding cannot be changed once it's been set.
## According to SQLite3 documentation, any attempt to change ## According to SQLite3 documentation, any attempt to change
## the encoding after the database is created will be silently ## the encoding after the database is created will be silently
## ignored. ## ignored.
exec(connection, sql"PRAGMA encoding = ?", [encoding]) exec(connection, sql"PRAGMA encoding = ?", [encoding])
result = connection.getValue(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 ## This module implements graphical output for Nim; the current
## implementation uses SDL but the interface is meant to support multiple ## 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. ## implicitly.
import colors, math import colors, math
@ -24,7 +24,7 @@ type
Surface* {.pure, final.} = object Surface* {.pure, final.} = object
w*, h*: Natural w*, h*: Natural
s*: sdl.PSurface s*: sdl.PSurface
EGraphics* = object of IOError EGraphics* = object of IOError
Font {.pure, final.} = object Font {.pure, final.} = object
@ -35,7 +35,7 @@ type
proc toSdlColor*(c: Color): sdl.Color = proc toSdlColor*(c: Color): sdl.Color =
## Convert colors.Color to sdl.Color ## Convert colors.Color to sdl.Color
var x = c.extractRGB var x = c.extractRGB
result.r = x.r and 0xff result.r = x.r and 0xff
result.g = x.g and 0xff result.g = x.g and 0xff
result.b = x.b 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 = proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
## Creates a color using ``sdl.MapRGBA``. ## Creates a color using ``sdl.MapRGBA``.
var x = c.extractRGB 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) x.b and 0xff, alpha and 0xff)
proc toSdlRect*(r: Rect): sdl.Rect = proc toSdlRect*(r: Rect): sdl.Rect =
@ -53,26 +53,26 @@ proc toSdlRect*(r: Rect): sdl.Rect =
result.w = uint16(r.width) result.w = uint16(r.width)
result.h = uint16(r.height) result.h = uint16(r.height)
proc raiseEGraphics = proc raiseEGraphics =
raise newException(EGraphics, $sdl.getError()) raise newException(EGraphics, $sdl.getError())
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s) proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
proc newSurface*(width, height: int): PSurface = proc newSurface*(width, height: int): PSurface =
## creates a new surface. ## creates a new surface.
new(result, surfaceFinalizer) new(result, surfaceFinalizer)
result.w = width result.w = width
result.h = height result.h = height
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height, result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0) 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
if result.s == nil: if result.s == nil:
raiseEGraphics() raiseEGraphics()
assert(not sdl.mustLock(result.s)) assert(not sdl.mustLock(result.s))
proc fontFinalizer(f: PFont) = closeFont(f.f) 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. ## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
new(result, fontFinalizer) new(result, fontFinalizer)
result.f = openFont(name, size.cint) result.f = openFont(name, size.cint)
@ -84,7 +84,7 @@ var
defaultFont*: PFont ## default font that is used; this needs to initialized defaultFont*: PFont ## default font that is used; this needs to initialized
## by the client! ## 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. ## initializes the `defaultFont` var.
defaultFont = newFont(name, size, color) defaultFont = newFont(name, size, color)
@ -98,7 +98,7 @@ proc newScreenSurface*(width, height: int): PSurface =
raiseEGraphics() raiseEGraphics()
proc writeToBMP*(sur: PSurface, filename: string) = 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. ## BMP file.
if sdl.saveBMP(sur.s, filename) != 0: if sdl.saveBMP(sur.s, filename) != 0:
raise newException(IOError, "cannot write: " & filename) raise newException(IOError, "cannot write: " & filename)
@ -111,7 +111,7 @@ type
template setPix(video, pitch, x, y, col: expr): stmt = template setPix(video, pitch, x, y, col: expr): stmt =
video[y * pitch + x] = int32(col) video[y * pitch + x] = int32(col)
template getPix(video, pitch, x, y: expr): expr = template getPix(video, pitch, x, y: expr): expr =
colors.Color(video[y * pitch + x]) colors.Color(video[y * pitch + x])
const const
@ -120,7 +120,7 @@ const
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} = proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
assert x <% sur.w assert x <% sur.w
assert y <% sur.h 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) x, y)
proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} = 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! ## set the pixel at position ``(x, y)``. No range checking is done!
setPixel(sur, x, y, col) setPixel(sur, x, y, col)
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface, proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
srcRect: Rect) = srcRect: Rect) =
## Copies ``srcSurf`` into ``destSurf`` ## Copies ``srcSurf`` into ``destSurf``
var destTRect, srcTRect: sdl.Rect var destTRect, srcTRect: sdl.Rect
@ -175,7 +175,7 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
## font. ## font.
var textSur: PSurface # This surface will have the text drawn on it var textSur: PSurface # This surface will have the text drawn on it
new(textSur, surfaceFinalizer) new(textSur, surfaceFinalizer)
# Render the text # Render the text
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color) textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
# Merge the text surface with sur # 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, proc drawText*(sur: PSurface, p: Point, text: string,
bg: Color, font = defaultFont) = bg: Color, font = defaultFont) =
## Draws text, at location ``p`` with font ``font``. ``bg`` ## Draws text, at location ``p`` with font ``font``. ``bg``
## is the background color. ## is the background color.
var textSur: PSurface # This surface will have the text drawn on it var textSur: PSurface # This surface will have the text drawn on it
new(textSur, surfaceFinalizer) new(textSur, surfaceFinalizer)
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg)) textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
# Merge the text surface with sur # Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) = proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color ## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur`. ## onto the surface `sur`.
@ -205,7 +205,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
if x+px <% sur.w: 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 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 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 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 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 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 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 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 py = py - 1
px = px + 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 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 return if val < 0: 0 elif val >= s.h: s.h-1 else: val
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) = 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 dy = y1 - y0
var dx = x1 - x0 var dx = x1 - x0
if dy < 0: if dy < 0:
dy = -dy dy = -dy
stepy = -1 stepy = -1
else: else:
stepy = 1 stepy = 1
@ -251,7 +251,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
stepx = -1 stepx = -1
else: else:
stepx = 1 stepx = 1
dy = dy * 2 dy = dy * 2
dx = dx * 2 dx = dx * 2
var video = cast[PPixels](sur.s.pixels) var video = cast[PPixels](sur.s.pixels)
var pitch = sur.s.pitch.int div ColSize var pitch = sur.s.pitch.int div ColSize
@ -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): 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 minW = min(sur.s.w - r.x, r.width)
var minH = min(sur.s.h - r.y, r.height) var minH = min(sur.s.h - r.y, r.height)
# Draw Top # Draw Top
for i in 0 .. minW - 1: for i in 0 .. minW - 1:
setPix(video, pitch, r.x + i, r.y, color) setPix(video, pitch, r.x + i, r.y, color)
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
# Draw left side # Draw left side
for i in 0 .. minH - 1: for i in 0 .. minH - 1:
setPix(video, pitch, r.x, r.y + i, color) setPix(video, pitch, r.x, r.y + i, color)
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
proc fillRect*(sur: PSurface, r: Rect, col: Color) = proc fillRect*(sur: PSurface, r: Rect, col: Color) =
## Fills a rectangle using sdl's ``FillRect`` function. ## Fills a rectangle using sdl's ``FillRect`` function.
var rect = toSdlRect(r) var rect = toSdlRect(r)
@ -350,23 +350,23 @@ proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) =
var pitch = sur.s.pitch.int div ColSize var pitch = sur.s.pitch.int div ColSize
if cx+x <= sur.s.w-1: 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 cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
if cx-x <= sur.s.w-1: 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 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) = 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 ## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
## of the ellipse. ## of the ellipse.
var var
x, y: Natural x, y: Natural
xChange, yChange: int xChange, yChange: int
ellipseError: Natural ellipseError: Natural
twoASquare, twoBSquare: Natural twoASquare, twoBSquare: Natural
stoppingX, stoppingY: Natural stoppingX, stoppingY: Natural
twoASquare = 2 * xRadius * xRadius twoASquare = 2 * xRadius * xRadius
twoBSquare = 2 * yRadius * yRadius twoBSquare = 2 * yRadius * yRadius
x = xRadius x = xRadius
@ -376,7 +376,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
ellipseError = 0 ellipseError = 0
stoppingX = twoBSquare * xRadius stoppingX = twoBSquare * xRadius
stoppingY = 0 stoppingY = 0
while stoppingX >= stoppingY: # 1st set of points, y` > - 1 while stoppingX >= stoppingY: # 1st set of points, y` > - 1
sur.plot4EllipsePoints(cx, cy, x, y, col) sur.plot4EllipsePoints(cx, cy, x, y, col)
inc(y) inc(y)
@ -388,7 +388,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
dec(stoppingX, twoBSquare) dec(stoppingX, twoBSquare)
inc(ellipseError, xChange) inc(ellipseError, xChange)
inc(xChange, twoBSquare) inc(xChange, twoBSquare)
# 1st point set is done; start the 2nd set of points # 1st point set is done; start the 2nd set of points
x = 0 x = 0
y = yRadius y = yRadius
@ -408,7 +408,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
dec(stoppingY, twoASquare) dec(stoppingY, twoASquare)
inc(ellipseError, yChange) inc(ellipseError, yChange)
inc(yChange,twoASquare) inc(yChange,twoASquare)
proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) = 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): 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, setPix(video, pitch, x, y,
pixColor.intensity(1.0 - c) + color.intensity(c)) 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 cround(x: expr): expr = ipart(x + 0.5)
template fpart(x: expr): expr = x - ipart(x) template fpart(x: expr): expr = x - ipart(x)
template rfpart(x: expr): expr = 1.0 - fpart(x) template rfpart(x: expr): expr = 1.0 - fpart(x)
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) = proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's ## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
## line algorithm ## line algorithm
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(), var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
p1.y.toFloat(), p2.y.toFloat()) p1.y.toFloat(), p2.y.toFloat())
var dx = x2 - x1 var dx = x2 - x1
var dy = y2 - y1 var dy = y2 - y1
var ax = dx var ax = dx
if ax < 0'f64: if ax < 0'f64:
ax = 0'f64 - ax ax = 0'f64 - ax
var ay = dy var ay = dy
if ay < 0'f64: if ay < 0'f64:
ay = 0'f64 - ay ay = 0'f64 - ay
if ax < ay: if ax < ay:
swap(x1, y1) swap(x1, y1)
swap(x2, y2) swap(x2, y2)
swap(dx, dy) swap(dx, dy)
template doPlot(x, y: int, c: float, color: Color): stmt = template doPlot(x, y: int, c: float, color: Color): stmt =
if ax < ay: if ax < ay:
sur.plotAA(y, x, c, color) sur.plotAA(y, x, c, color)
else: else:
sur.plotAA(x, y, c, color) sur.plotAA(x, y, c, color)
if x2 < x1: if x2 < x1:
swap(x1, x2) swap(x1, x2)
swap(y1, y2) swap(y1, y2)
var gradient = dy / dx var gradient = dy / dx
# handle first endpoint # handle first endpoint
var xend = cround(x1) var xend = cround(x1)
@ -509,19 +509,19 @@ when not defined(testing) and isMainModule:
# Draw the shapes # Draw the shapes
surf.drawLineAA((150, 170), (400, 471), colTan) surf.drawLineAA((150, 170), (400, 471), colTan)
surf.drawLine((100, 170), (400, 471), colRed) surf.drawLine((100, 170), (400, 471), colRed)
surf.drawEllipse(200, 300, 200, 30, colSeaGreen) 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. # 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.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
surf.drawEllipse(400, 300, 300, 300, colOrange) surf.drawEllipse(400, 300, 300, 300, colOrange)
surf.drawEllipse(5, 5, 5, 5, colGreen) surf.drawEllipse(5, 5, 5, 5, colGreen)
surf.drawHorLine(5, 5, 900, colRed) surf.drawHorLine(5, 5, 900, colRed)
surf.drawVerLine(5, 60, 800, colRed) surf.drawVerLine(5, 60, 800, colRed)
surf.drawCircle((600, 500), 60, colRed) surf.drawCircle((600, 500), 60, colRed)
surf.fillRect((50, 50, 100, 100), colFuchsia) surf.fillRect((50, 50, 100, 100), colFuchsia)
surf.fillRect((150, 50, 100, 100), colGreen) surf.fillRect((150, 50, 100, 100), colGreen)
surf.drawRect((50, 150, 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.drawHorLine(250, 150, 100, colRed)
surf.drawLineAA((592, 160), (592, 280), colPurple) surf.drawLineAA((592, 160), (592, 280), colPurple)
#surf.drawText((300, 300), "TEST", colMidnightBlue) #surf.drawText((300, 300), "TEST", colMidnightBlue)
#var textSize = textBounds("TEST") #var textSize = textBounds("TEST")
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " & #surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
# $textSize.height, colDarkGreen) # $textSize.height, colDarkGreen)
var mouseStartX = -1 var mouseStartX = -1
var mouseStartY = -1 var mouseStartY = -1
withEvents(surf, event): withEvents(surf, event):
@ -561,17 +561,17 @@ when not defined(testing) and isMainModule:
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple) surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
mouseStartX = -1 mouseStartX = -1
mouseStartY = -1 mouseStartY = -1
of sdl.MOUSEMOTION: of sdl.MOUSEMOTION:
var mm = sdl.evMouseMotion(eventp) var mm = sdl.evMouseMotion(eventp)
if mouseStartX != -1 and mouseStartY != -1: if mouseStartX != -1 and mouseStartY != -1:
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple) surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
#echo(mm.x, " ", mm.y, " ", mm.yrel) #echo(mm.x, " ", mm.y, " ", mm.yrel)
else: else:
discard "echo(event.kind)" discard "echo(event.kind)"
sdl.updateRect(surf.s, 0, 0, 800, 600) sdl.updateRect(surf.s, 0, 0, 800, 600)
surf.writeToBMP("test.bmp") surf.writeToBMP("test.bmp")
sdl.quit() sdl.quit()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # 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. ## the `iconv`:idx: library, on Windows the Windows API.
import os, parseutils, strutils import os, parseutils, strutils
@ -41,173 +41,173 @@ when defined(windows):
inc j inc j
result = i == a.len and j == b.len result = i == a.len and j == b.len
const const
winEncodings = [ winEncodings = [
(1, "OEMCP"), # current OEM codepage (1, "OEMCP"), # current OEM codepage
(037, "IBM037"), # IBM EBCDIC US-Canada (037, "IBM037"), # IBM EBCDIC US-Canada
(437, "IBM437"), # OEM United States (437, "IBM437"), # OEM United States
(500, "IBM500"), # IBM EBCDIC International (500, "IBM500"), # IBM EBCDIC International
(708, "ASMO-708"), # Arabic (ASMO 708) (708, "ASMO-708"), # Arabic (ASMO 708)
(709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4) (709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4)
(710, ""), # Arabic - Transparent Arabic (710, ""), # Arabic - Transparent Arabic
(720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS) (720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS)
(737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS) (737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS)
(775, "ibm775"), # OEM Baltic; Baltic (DOS) (775, "ibm775"), # OEM Baltic; Baltic (DOS)
(850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS) (850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS)
(852, "ibm852"), # OEM Latin 2; Central European (DOS) (852, "ibm852"), # OEM Latin 2; Central European (DOS)
(855, "IBM855"), # OEM Cyrillic (primarily Russian) (855, "IBM855"), # OEM Cyrillic (primarily Russian)
(857, "ibm857"), # OEM Turkish; Turkish (DOS) (857, "ibm857"), # OEM Turkish; Turkish (DOS)
(858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol (858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol
(860, "IBM860"), # OEM Portuguese; Portuguese (DOS) (860, "IBM860"), # OEM Portuguese; Portuguese (DOS)
(861, "ibm861"), # OEM Icelandic; Icelandic (DOS) (861, "ibm861"), # OEM Icelandic; Icelandic (DOS)
(862, "DOS-862"), # OEM Hebrew; Hebrew (DOS) (862, "DOS-862"), # OEM Hebrew; Hebrew (DOS)
(863, "IBM863"), # OEM French Canadian; French Canadian (DOS) (863, "IBM863"), # OEM French Canadian; French Canadian (DOS)
(864, "IBM864"), # OEM Arabic; Arabic (864) (864, "IBM864"), # OEM Arabic; Arabic (864)
(865, "IBM865"), # OEM Nordic; Nordic (DOS) (865, "IBM865"), # OEM Nordic; Nordic (DOS)
(866, "cp866"), # OEM Russian; Cyrillic (DOS) (866, "cp866"), # OEM Russian; Cyrillic (DOS)
(869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS) (869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS)
(870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 (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) (874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
(875, "cp875"), # IBM EBCDIC Greek Modern (875, "cp875"), # IBM EBCDIC Greek Modern
(932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS) (932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS)
(936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) (936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
(949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code) (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) (950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
(1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5) (1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5)
(1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System (1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System
(1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) (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) (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) (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) (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) (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) (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) (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) (1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
(1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-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) (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 (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 (1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications
(1250, "windows-1250"), # ANSI Central European; Central European (Windows) (1250, "windows-1250"), # ANSI Central European; Central European (Windows)
(1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows) (1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows)
(1252, "windows-1252"), # ANSI Latin 1; Western European (Windows) (1252, "windows-1252"), # ANSI Latin 1; Western European (Windows)
(1253, "windows-1253"), # ANSI Greek; Greek (Windows) (1253, "windows-1253"), # ANSI Greek; Greek (Windows)
(1254, "windows-1254"), # ANSI Turkish; Turkish (Windows) (1254, "windows-1254"), # ANSI Turkish; Turkish (Windows)
(1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows) (1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows)
(1256, "windows-1256"), # ANSI Arabic; Arabic (Windows) (1256, "windows-1256"), # ANSI Arabic; Arabic (Windows)
(1257, "windows-1257"), # ANSI Baltic; Baltic (Windows) (1257, "windows-1257"), # ANSI Baltic; Baltic (Windows)
(1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows) (1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
(1250, "cp-1250"), # ANSI Central European; Central European (Windows) (1250, "cp-1250"), # ANSI Central European; Central European (Windows)
(1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows) (1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows)
(1252, "cp-1252"), # ANSI Latin 1; Western European (Windows) (1252, "cp-1252"), # ANSI Latin 1; Western European (Windows)
(1253, "cp-1253"), # ANSI Greek; Greek (Windows) (1253, "cp-1253"), # ANSI Greek; Greek (Windows)
(1254, "cp-1254"), # ANSI Turkish; Turkish (Windows) (1254, "cp-1254"), # ANSI Turkish; Turkish (Windows)
(1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows) (1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows)
(1256, "cp-1256"), # ANSI Arabic; Arabic (Windows) (1256, "cp-1256"), # ANSI Arabic; Arabic (Windows)
(1257, "cp-1257"), # ANSI Baltic; Baltic (Windows) (1257, "cp-1257"), # ANSI Baltic; Baltic (Windows)
(1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (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: when false:
# not needed yet: # not needed yet:
type type
@ -219,22 +219,22 @@ when defined(windows):
proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {. proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
stdcall, importc: "GetCPInfo", dynlib: "kernel32".} stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
proc nameToCodePage(name: string): CodePage = proc nameToCodePage(name: string): CodePage =
var nameAsInt: int var nameAsInt: int
if parseInt(name, nameAsInt) == 0: nameAsInt = -1 if parseInt(name, nameAsInt) == 0: nameAsInt = -1
for no, na in items(winEncodings): for no, na in items(winEncodings):
if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no) if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
result = CodePage(-1) result = CodePage(-1)
proc codePageToName(c: CodePage): string = proc codePageToName(c: CodePage): string =
for no, na in items(winEncodings): for no, na in items(winEncodings):
if no == int(c): if no == int(c):
return if na.len != 0: na else: $no return if na.len != 0: na else: $no
result = "" result = ""
proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".} proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
proc multiByteToWideChar( proc multiByteToWideChar(
codePage: CodePage, codePage: CodePage,
dwFlags: int32, dwFlags: int32,
@ -254,7 +254,7 @@ when defined(windows):
lpDefaultChar: cstring=nil, lpDefaultChar: cstring=nil,
lpUsedDefaultChar: pointer=nil): cint {. lpUsedDefaultChar: pointer=nil): cint {.
stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".} stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
else: else:
when defined(haiku): when defined(haiku):
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)" const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
@ -292,31 +292,31 @@ else:
proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer, proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer,
outbuf: var cstring, outbytesLeft: var int): int {. outbuf: var cstring, outbytesLeft: var int): int {.
importc: prefix & "iconv", cdecl, dynlib: iconvDll.} importc: prefix & "iconv", cdecl, dynlib: iconvDll.}
proc getCurrentEncoding*(): string = proc getCurrentEncoding*(): string =
## retrieves the current encoding. On Unix, always "UTF-8" is returned. ## retrieves the current encoding. On Unix, always "UTF-8" is returned.
when defined(windows): when defined(windows):
result = codePageToName(getACP()) result = codePageToName(getACP())
else: else:
result = "UTF-8" result = "UTF-8"
proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
## opens a converter that can convert from `srcEncoding` to `destEncoding`. ## opens a converter that can convert from `srcEncoding` to `destEncoding`.
## Raises `EIO` if it cannot fulfill the request. ## Raises `EIO` if it cannot fulfill the request.
when not defined(windows): when not defined(windows):
result = iconvOpen(destEncoding, srcEncoding) result = iconvOpen(destEncoding, srcEncoding)
if result == nil: if result == nil:
raise newException(EncodingError, raise newException(EncodingError,
"cannot create encoding converter from " & "cannot create encoding converter from " &
srcEncoding & " to " & destEncoding) srcEncoding & " to " & destEncoding)
else: else:
result.dest = nameToCodePage(destEncoding) result.dest = nameToCodePage(destEncoding)
result.src = nameToCodePage(srcEncoding) result.src = nameToCodePage(srcEncoding)
if int(result.dest) == -1: if int(result.dest) == -1:
raise newException(EncodingError, raise newException(EncodingError,
"cannot find encoding " & destEncoding) "cannot find encoding " & destEncoding)
if int(result.src) == -1: if int(result.src) == -1:
raise newException(EncodingError, raise newException(EncodingError,
"cannot find encoding " & srcEncoding) "cannot find encoding " & srcEncoding)
proc close*(c: EncodingConverter) = proc close*(c: EncodingConverter) =
@ -328,7 +328,7 @@ when defined(windows):
proc convert*(c: EncodingConverter, s: string): string = proc convert*(c: EncodingConverter, s: string): string =
## converts `s` to `destEncoding` that was given to the converter `c`. It ## converts `s` to `destEncoding` that was given to the converter `c`. It
## assumed that `s` is in `srcEncoding`. ## assumed that `s` is in `srcEncoding`.
# special case: empty string: needed because MultiByteToWideChar # special case: empty string: needed because MultiByteToWideChar
# return 0 in case of error: # return 0 in case of error:
if s.len == 0: return "" if s.len == 0: return ""
@ -336,21 +336,21 @@ when defined(windows):
var cap = s.len + s.len shr 2 var cap = s.len + s.len shr 2
result = newStringOfCap(cap*2) result = newStringOfCap(cap*2)
# convert to utf-16 LE # 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), lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len), cbMultiByte = cint(s.len),
lpWideCharStr = cstring(result), lpWideCharStr = cstring(result),
cchWideChar = cint(cap)) cchWideChar = cint(cap))
if m == 0: if m == 0:
# try again; ask for capacity: # try again; ask for capacity:
cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s), lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len), cbMultiByte = cint(s.len),
lpWideCharStr = nil, lpWideCharStr = nil,
cchWideChar = cint(0)) cchWideChar = cint(0))
# and do the conversion properly: # and do the conversion properly:
result = newStringOfCap(cap*2) result = newStringOfCap(cap*2)
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s), lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len), cbMultiByte = cint(s.len),
lpWideCharStr = cstring(result), lpWideCharStr = cstring(result),
@ -361,7 +361,7 @@ when defined(windows):
setLen(result, m*2) setLen(result, m*2)
else: else:
assert(false) # cannot happen assert(false) # cannot happen
# if already utf-16 LE, no further need to do something: # if already utf-16 LE, no further need to do something:
if int(c.dest) == 1200: return if int(c.dest) == 1200: return
# otherwise the fun starts again: # otherwise the fun starts again:
@ -428,7 +428,7 @@ else:
outLen = len(result) - offset outLen = len(result) - offset
else: else:
raiseOSError(lerr.OSErrorCode) 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' # not '\0'
discard iconv(c, nil, nil, dst, outLen) discard iconv(c, nil, nil, dst, outLen)
if iconvres == cint(-1) and errno == E2BIG: if iconvres == cint(-1) and errno == E2BIG:
@ -441,7 +441,7 @@ else:
# trim output buffer # trim output buffer
setLen(result, len(result) - outLen) setLen(result, len(result) - outLen)
proc convert*(s: string, destEncoding = "UTF-8", proc convert*(s: string, destEncoding = "UTF-8",
srcEncoding = "CP1252"): string = srcEncoding = "CP1252"): string =
## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`. ## 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 ## 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[6] = i[1]
o[7] = i[0] 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 ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
## contain at least 4 bytes. ## contain at least 4 bytes.
var i = cast[cstring](inp) var i = cast[cstring](inp)
@ -34,7 +34,7 @@ proc swapEndian32*(outp, inp: pointer) =
o[2] = i[1] o[2] = i[1]
o[3] = i[0] 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 ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
## contain at least 2 bytes. ## contain at least 2 bytes.
var var
@ -50,7 +50,7 @@ when system.cpuEndian == bigEndian:
proc bigEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8) proc bigEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
proc bigEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4) proc bigEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2) proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2)
else: else:
proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8) proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4) proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2) 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 # Used by pure/hashes.nim, and the compiler parsing
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } = 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 + 1] == '\128' and
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen

View file

@ -10,7 +10,7 @@
## :Author: Alex Mitchell ## :Author: Alex Mitchell
## ##
## This module implements an event system that is not dependent on external ## 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 ## 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. ## events: one is a python-inspired way; the other is more of a C-style way.
## ##
@ -23,7 +23,7 @@
## # Python way ## # Python way
## ee.on("EventName", handleevent) ## ee.on("EventName", handleevent)
## ee.emit("EventName", genericargs) ## ee.emit("EventName", genericargs)
## ##
## # C/Java way ## # C/Java way
## # Declare a type ## # Declare a type
## type ## type
@ -45,7 +45,7 @@ type
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler, {.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
TEventEmitter: EventEmitter, EInvalidEvent: EventError].} TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
proc initEventHandler*(name: string): EventHandler = proc initEventHandler*(name: string): EventHandler =
## Initializes an EventHandler with the specified name and returns it. ## Initializes an EventHandler with the specified name and returns it.
result.handlers = @[] result.handlers = @[]
@ -61,7 +61,7 @@ proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.
if fn == handler.handlers[i]: if fn == handler.handlers[i]:
handler.handlers.del(i) handler.handlers.del(i)
break break
proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool = proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool =
## Checks if a callback is registered to this event handler. ## Checks if a callback is registered to this event handler.
return handler.handlers.contains(fn) 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) emitter.s.add(eh)
else: else:
addHandler(emitter.s[i], fn) addHandler(emitter.s[i], fn)
proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler, proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler,
args: EventArgs) = args: EventArgs) =
## Fires an event handler with specified event arguments. ## Fires an event handler with specified event arguments.
for fn in items(eventhandler.handlers): fn(args) for fn in items(eventhandler.handlers): fn(args)

View file

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

View file

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

View file

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

View file

@ -33,7 +33,7 @@ type
when defined(windows): when defined(windows):
fHandle: int fHandle: int
mapHandle: int mapHandle: int
else: else:
handle: cint handle: cint
@ -131,7 +131,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
fail(osLastError(), "error opening file") fail(osLastError(), "error opening file")
if newFileSize != -1: if newFileSize != -1:
var var
sizeHigh = int32(newFileSize shr 32) sizeHigh = int32(newFileSize shr 32)
sizeLow = int32(newFileSize and 0xffffffff) sizeLow = int32(newFileSize and 0xffffffff)
@ -177,7 +177,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
rollback() rollback()
if result.handle != 0: discard close(result.handle) if result.handle != 0: discard close(result.handle)
raiseOSError(errCode) raiseOSError(errCode)
var flags = if readonly: O_RDONLY else: O_RDWR var flags = if readonly: O_RDONLY else: O_RDWR
if newFileSize != -1: if newFileSize != -1:
@ -221,7 +221,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
proc close*(f: var MemFile) = proc close*(f: var MemFile) =
## closes the memory mapped file `f`. All changes are written back to the ## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access. ## file system, if `f` was opened with write access.
var error = false var error = false
var lastErr: OSErrorCode var lastErr: OSErrorCode
@ -245,7 +245,7 @@ proc close*(f: var MemFile) =
f.mapHandle = 0 f.mapHandle = 0
else: else:
f.handle = 0 f.handle = 0
if error: raiseOSError(lastErr) if error: raiseOSError(lastErr)
type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1285,7 +1285,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
result = a.len - b.len result = a.len - b.len
proc reversed*(s: string): string = 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: ## Unicode combining characters are correctly interpreted as well:
## ##
## .. code-block:: nim ## .. code-block:: nim

View file

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

View file

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

View file

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

View file

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

View file

@ -16,7 +16,7 @@ proc lowGauge(n: PAvlNode): int =
while not isBottom(it): while not isBottom(it):
result = it.key result = it.key
it = it.link[0] it = it.link[0]
proc highGauge(n: PAvlNode): int = proc highGauge(n: PAvlNode): int =
result = -1 result = -1
var it = n var it = n
@ -24,7 +24,7 @@ proc highGauge(n: PAvlNode): int =
result = it.upperBound result = it.upperBound
it = it.link[1] it = it.link[1]
proc find(root: PAvlNode, key: int): PAvlNode = proc find(root: PAvlNode, key: int): PAvlNode =
var it = root var it = root
while not isBottom(it): while not isBottom(it):
if it.key == key: return 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.cap = cap
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell))) s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
proc deinit(s: var CellSeq) = proc deinit(s: var CellSeq) =
dealloc(s.d) dealloc(s.d)
s.d = nil s.d = nil
s.len = 0 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 # For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on # generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof). # random-number generation for proof).
proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc = proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
var h = cast[int](key) and t.max var h = cast[int](key) and t.max
while t.data[h] != nil: while t.data[h] != nil:
@ -170,16 +170,16 @@ proc excl(s: var CellSet, cell: PCell) =
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
not (1 shl (u and IntMask))) not (1 shl (u and IntMask)))
proc containsOrIncl(s: var CellSet, cell: PCell): bool = proc containsOrIncl(s: var CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell) var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift) var t = cellSetGet(s, u shr PageShift)
if t != nil: if t != nil:
u = (u %% PageSize) /% MemAlign u = (u %% PageSize) /% MemAlign
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0 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 t.bits[u shr IntShift] = t.bits[u shr IntShift] or
(1 shl (u and IntMask)) (1 shl (u and IntMask))
else: else:
incl(s, cell) incl(s, cell)
result = false result = false

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,7 +40,7 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
while it != nil: while it != nil:
inc(total) inc(total)
it = it.prev 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 b != nil: b = b.prev
if total != i: if total != i:
st[i] = "..." st[i] = "..."

View file

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

View file

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

View file

@ -34,8 +34,8 @@ when defined(windows):
elif defined(macosx): elif defined(macosx):
type type
MachTimebaseInfoData {.pure, final, MachTimebaseInfoData {.pure, final,
importc: "mach_timebase_info_data_t", importc: "mach_timebase_info_data_t",
header: "<mach/mach_time.h>".} = object header: "<mach/mach_time.h>".} = object
numer, denom: int32 numer, denom: int32
{.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].} {.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
@ -46,10 +46,10 @@ elif defined(macosx):
proc getTicks(): Ticks {.inline.} = proc getTicks(): Ticks {.inline.} =
result = Ticks(mach_absolute_time()) result = Ticks(mach_absolute_time())
var timeBaseInfo: MachTimebaseInfoData var timeBaseInfo: MachTimebaseInfoData
mach_timebase_info(timeBaseInfo) mach_timebase_info(timeBaseInfo)
proc `-`(a, b: Ticks): Nanos = proc `-`(a, b: Ticks): Nanos =
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
@ -57,10 +57,10 @@ elif defined(posixRealtime):
type type
Clockid {.importc: "clockid_t", header: "<time.h>", final.} = object 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 final, pure.} = object ## struct timespec
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_nsec: int ## Nanoseconds. tv_nsec: int ## Nanoseconds.
{.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].} {.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
var var
@ -77,12 +77,12 @@ elif defined(posixRealtime):
proc `-`(a, b: Ticks): Nanos {.borrow.} proc `-`(a, b: Ticks): Nanos {.borrow.}
else: else:
# fallback Posix implementation: # fallback Posix implementation:
type type
Timeval {.importc: "struct timeval", header: "<sys/select.h>", Timeval {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval final, pure.} = object ## struct timeval
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_usec: int ## Microseconds. tv_usec: int ## Microseconds.
{.deprecated: [Ttimeval: Timeval].} {.deprecated: [Ttimeval: Timeval].}
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {. proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>".} importc: "gettimeofday", header: "<sys/time.h>".}
@ -90,7 +90,7 @@ else:
proc getTicks(): Ticks = proc getTicks(): Ticks =
var t: Timeval var t: Timeval
posix_gettimeofday(t) 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) int64(t.tv_usec) * 1000'i64)
proc `-`(a, b: Ticks): Nanos {.borrow.} 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 ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
# If the 16 bits following the high surrogate are in the source buffer... # If the 16 bits following the high surrogate are in the source buffer...
let ch2 = int(cast[uint16](w[i])) let ch2 = int(cast[uint16](w[i]))
# If it's a low surrogate, convert to UTF32: # If it's a low surrogate, convert to UTF32:
if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END: if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase 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: elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
#invalid UTF-16 #invalid UTF-16
ch = replacement ch = replacement
if ch < 0x80: if ch < 0x80:
result.add chr(ch) result.add chr(ch)
elif ch < 0x800: 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 12 or 0b1110_0000)
result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00) result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
result.add chr(0xFFFD and ones(6) or 0b10_0000_00) result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
proc `$`*(s: WideCString): string = proc `$`*(s: WideCString): string =
result = s $ 80 result = s $ 80

View file

@ -10,9 +10,9 @@
## This module is a low level wrapper for `libsvm`:idx:. ## This module is a low level wrapper for `libsvm`:idx:.
{.deadCodeElim: on.} {.deadCodeElim: on.}
const const
LIBSVM_VERSION* = 312 LIBSVM_VERSION* = 312
when defined(windows): when defined(windows):
const svmdll* = "libsvm.dll" const svmdll* = "libsvm.dll"
elif defined(macosx): elif defined(macosx):
@ -20,97 +20,97 @@ elif defined(macosx):
else: else:
const svmdll* = "libsvm.so" const svmdll* = "libsvm.so"
type type
Node*{.pure, final.} = object Node*{.pure, final.} = object
index*: cint index*: cint
value*: cdouble value*: cdouble
Problem*{.pure, final.} = object Problem*{.pure, final.} = object
L*: cint L*: cint
y*: ptr cdouble y*: ptr cdouble
x*: ptr ptr Node x*: ptr ptr Node
Type*{.size: sizeof(cint).} = enum Type*{.size: sizeof(cint).} = enum
C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR 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 LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED
Parameter*{.pure, final.} = object Parameter*{.pure, final.} = object
typ*: Type typ*: Type
kernelType*: KernelType kernelType*: KernelType
degree*: cint # for poly degree*: cint # for poly
gamma*: cdouble # for poly/rbf/sigmoid gamma*: cdouble # for poly/rbf/sigmoid
coef0*: cdouble # for poly/sigmoid coef0*: cdouble # for poly/sigmoid
# these are for training only # these are for training only
cache_size*: cdouble # in MB cache_size*: cdouble # in MB
eps*: cdouble # stopping criteria eps*: cdouble # stopping criteria
C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR
nr_weight*: cint # for C_SVC nr_weight*: cint # for C_SVC
weight_label*: ptr cint # for C_SVC weight_label*: ptr cint # for C_SVC
weight*: ptr cdouble # for C_SVC weight*: ptr cdouble # for C_SVC
nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR
p*: cdouble # for EPSILON_SVR p*: cdouble # for EPSILON_SVR
shrinking*: cint # use the shrinking heuristics shrinking*: cint # use the shrinking heuristics
probability*: cint # do probability estimates probability*: cint # do probability estimates
{.deprecated: [Tnode: Node, Tproblem: Problem, Ttype: Type, {.deprecated: [Tnode: Node, Tproblem: Problem, Ttype: Type,
TKernelType: KernelType, Tparameter: Parameter].} TKernelType: KernelType, Tparameter: Parameter].}
# #
# svm_model # svm_model
# #
type type
Model*{.pure, final.} = object Model*{.pure, final.} = object
param*: Parameter # parameter param*: Parameter # parameter
nr_class*: cint # number of classes, = 2 in regression/one class svm nr_class*: cint # number of classes, = 2 in regression/one class svm
L*: cint # total #SV L*: cint # total #SV
SV*: ptr ptr Node # SVs (SV[l]) SV*: ptr ptr Node # SVs (SV[l])
sv_coef*: ptr ptr cdouble # coefficients for SVs in decision functions (sv_coef[k-1][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]) rho*: ptr cdouble # constants in decision functions (rho[k*(k-1)/2])
probA*: ptr cdouble # pariwise probability information probA*: ptr cdouble # pariwise probability information
probB*: ptr cdouble # for classification only probB*: ptr cdouble # for classification only
label*: ptr cint # label of each class (label[k]) label*: ptr cint # label of each class (label[k])
nSV*: ptr cint # number of SVs for each class (nSV[k]) nSV*: ptr cint # number of SVs for each class (nSV[k])
# nSV[0] + nSV[1] + ... + nSV[k-1] = l # nSV[0] + nSV[1] + ... + nSV[k-1] = l
# XXX # XXX
free_sv*: cint # 1 if svm_model is created by svm_load_model free_sv*: cint # 1 if svm_model is created by svm_load_model
# 0 if svm_model is created by svm_train # 0 if svm_model is created by svm_train
{.deprecated: [TModel: Model].} {.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.} importc: "svm_train", dynlib: svmdll.}
proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint, proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint,
target: ptr cdouble){.cdecl, target: ptr cdouble){.cdecl,
importc: "svm_cross_validation", dynlib: svmdll.} 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.} 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.} 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.} 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.} 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.} 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.} importc: "svm_get_svr_probability", dynlib: svmdll.}
proc predict_values*(model: ptr Model, x: ptr Node, dec_values: ptr cdouble): cdouble{. proc predict_values*(model: ptr Model, x: ptr Node, dec_values: ptr cdouble): cdouble{.
cdecl, importc: "svm_predict_values", dynlib: svmdll.} 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.} importc: "svm_predict", dynlib: svmdll.}
proc predict_probability*(model: ptr Model, x: ptr Node, proc predict_probability*(model: ptr Model, x: ptr Node,
prob_estimates: ptr cdouble): cdouble{.cdecl, prob_estimates: ptr cdouble): cdouble{.cdecl,
importc: "svm_predict_probability", dynlib: svmdll.} 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.} 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.} 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.} dynlib: svmdll.}
proc check_parameter*(prob: ptr Problem, param: ptr Parameter): cstring{. proc check_parameter*(prob: ptr Problem, param: ptr Parameter): cstring{.
cdecl, importc: "svm_check_parameter", dynlib: svmdll.} 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.} importc: "svm_check_probability_model", dynlib: svmdll.}
proc set_print_string_function*(print_func: proc (arg: cstring) {.cdecl.}){. 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.} {.deadCodeElim: on.}
when defined(windows): when defined(windows):
const const
Lib = "sqlite3.dll" Lib = "sqlite3.dll"
elif defined(macosx): elif defined(macosx):
const const
Lib = "libsqlite3(|.0).dylib" Lib = "libsqlite3(|.0).dylib"
else: else:
const const
Lib = "libsqlite3.so(|.0)" Lib = "libsqlite3.so(|.0)"
const const
SQLITE_INTEGER* = 1 SQLITE_INTEGER* = 1
SQLITE_FLOAT* = 2 SQLITE_FLOAT* = 2
SQLITE_BLOB* = 4 SQLITE_BLOB* = 4
@ -26,38 +26,38 @@ const
SQLITE_TEXT* = 3 SQLITE_TEXT* = 3
SQLITE_UTF8* = 1 SQLITE_UTF8* = 1
SQLITE_UTF16LE* = 2 SQLITE_UTF16LE* = 2
SQLITE_UTF16BE* = 3 # Use native byte order SQLITE_UTF16BE* = 3 # Use native byte order
SQLITE_UTF16* = 4 # sqlite3_create_function only SQLITE_UTF16* = 4 # sqlite3_create_function only
SQLITE_ANY* = 5 #sqlite_exec return values SQLITE_ANY* = 5 #sqlite_exec return values
SQLITE_OK* = 0 SQLITE_OK* = 0
SQLITE_ERROR* = 1 # SQL error or missing database SQLITE_ERROR* = 1 # SQL error or missing database
SQLITE_INTERNAL* = 2 # An internal logic error in SQLite SQLITE_INTERNAL* = 2 # An internal logic error in SQLite
SQLITE_PERM* = 3 # Access permission denied SQLITE_PERM* = 3 # Access permission denied
SQLITE_ABORT* = 4 # Callback routine requested an abort SQLITE_ABORT* = 4 # Callback routine requested an abort
SQLITE_BUSY* = 5 # The database file is locked SQLITE_BUSY* = 5 # The database file is locked
SQLITE_LOCKED* = 6 # A table in the database is locked SQLITE_LOCKED* = 6 # A table in the database is locked
SQLITE_NOMEM* = 7 # A malloc() failed SQLITE_NOMEM* = 7 # A malloc() failed
SQLITE_READONLY* = 8 # Attempt to write a readonly database SQLITE_READONLY* = 8 # Attempt to write a readonly database
SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt() SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt()
SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred
SQLITE_CORRUPT* = 11 # The database disk image is malformed SQLITE_CORRUPT* = 11 # The database disk image is malformed
SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found
SQLITE_FULL* = 13 # Insertion failed because database is full SQLITE_FULL* = 13 # Insertion failed because database is full
SQLITE_CANTOPEN* = 14 # Unable to open the database file SQLITE_CANTOPEN* = 14 # Unable to open the database file
SQLITE_PROTOCOL* = 15 # Database lock protocol error SQLITE_PROTOCOL* = 15 # Database lock protocol error
SQLITE_EMPTY* = 16 # Database is empty SQLITE_EMPTY* = 16 # Database is empty
SQLITE_SCHEMA* = 17 # The database schema changed SQLITE_SCHEMA* = 17 # The database schema changed
SQLITE_TOOBIG* = 18 # Too much data for one row of a table SQLITE_TOOBIG* = 18 # Too much data for one row of a table
SQLITE_CONSTRAINT* = 19 # Abort due to contraint violation SQLITE_CONSTRAINT* = 19 # Abort due to contraint violation
SQLITE_MISMATCH* = 20 # Data type mismatch SQLITE_MISMATCH* = 20 # Data type mismatch
SQLITE_MISUSE* = 21 # Library used incorrectly SQLITE_MISUSE* = 21 # Library used incorrectly
SQLITE_NOLFS* = 22 # Uses OS features not supported on host SQLITE_NOLFS* = 22 # Uses OS features not supported on host
SQLITE_AUTH* = 23 # Authorization denied SQLITE_AUTH* = 23 # Authorization denied
SQLITE_FORMAT* = 24 # Auxiliary database format error SQLITE_FORMAT* = 24 # Auxiliary database format error
SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range
SQLITE_NOTADB* = 26 # File opened that is not a database file SQLITE_NOTADB* = 26 # File opened that is not a database file
SQLITE_ROW* = 100 # sqlite3_step() has another row ready SQLITE_ROW* = 100 # sqlite3_step() has another row ready
SQLITE_DONE* = 101 # sqlite3_step() has finished executing SQLITE_DONE* = 101 # sqlite3_step() has finished executing
SQLITE_COPY* = 0 SQLITE_COPY* = 0
SQLITE_CREATE_INDEX* = 1 SQLITE_CREATE_INDEX* = 1
SQLITE_CREATE_TABLE* = 2 SQLITE_CREATE_TABLE* = 2
@ -87,39 +87,39 @@ const
SQLITE_ALTER_TABLE* = 26 SQLITE_ALTER_TABLE* = 26
SQLITE_REINDEX* = 27 SQLITE_REINDEX* = 27
SQLITE_DENY* = 1 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_STATIC ((void(*)(void *))0)
#define SQLITE_TRANSIENT ((void(*)(void *))-1) #define SQLITE_TRANSIENT ((void(*)(void *))-1)
SQLITE_DETERMINISTIC* = 0x800 SQLITE_DETERMINISTIC* = 0x800
const const
SQLITE_STATIC* = nil SQLITE_STATIC* = nil
SQLITE_TRANSIENT* = cast[pointer](- 1) SQLITE_TRANSIENT* = cast[pointer](- 1)
type type
Sqlite3 {.pure, final.} = object Sqlite3 {.pure, final.} = object
PSqlite3* = ptr Sqlite3 PSqlite3* = ptr Sqlite3
PPSqlite3* = ptr PSqlite3 PPSqlite3* = ptr PSqlite3
Context{.pure, final.} = object Context{.pure, final.} = object
Pcontext* = ptr Context Pcontext* = ptr Context
Tstmt{.pure, final.} = object Tstmt{.pure, final.} = object
Pstmt* = ptr Tstmt Pstmt* = ptr Tstmt
Value{.pure, final.} = object Value{.pure, final.} = object
Pvalue* = ptr Value Pvalue* = ptr Value
PValueArg* = array[0..127, Pvalue] PValueArg* = array[0..127, Pvalue]
Callback* = proc (para1: pointer, para2: int32, para3, Callback* = proc (para1: pointer, para2: int32, para3,
para4: cstringArray): int32{.cdecl.} para4: cstringArray): int32{.cdecl.}
Tbind_destructor_func* = proc (para1: pointer){.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.} para3: PValueArg){.cdecl.}
Create_function_func_func* = proc (para1: Pcontext, para2: int32, Create_function_func_func* = proc (para1: Pcontext, para2: int32,
para3: PValueArg){.cdecl.} para3: PValueArg){.cdecl.}
Create_function_final_func* = proc (para1: Pcontext){.cdecl.} Create_function_final_func* = proc (para1: Pcontext){.cdecl.}
Result_func* = proc (para1: pointer){.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.} 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.} para4: cstring){.cdecl.}
{.deprecated: [TSqlite3: Sqlite3, TContext: Context, Tvalue: Value, {.deprecated: [TSqlite3: Sqlite3, TContext: Context, Tvalue: Value,
Tcallback: Callback, Tcreate_function_step_func: Create_function_step_func, Tcallback: Callback, Tcreate_function_step_func: Create_function_step_func,
@ -129,220 +129,220 @@ type
Tcollation_needed_func: Collation_needed_func].} Tcollation_needed_func: Collation_needed_func].}
proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".} proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".}
proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer, proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
errmsg: var cstring): int32{.cdecl, dynlib: Lib, errmsg: var cstring): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_exec".} 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".} importc: "sqlite3_last_insert_rowid".}
proc changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_changes".} 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".} importc: "sqlite3_total_changes".}
proc interrupt*(para1: PSqlite3){.cdecl, dynlib: Lib, importc: "sqlite3_interrupt".} 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".} importc: "sqlite3_complete".}
proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib, proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_complete16".} importc: "sqlite3_complete16".}
proc busy_handler*(para1: PSqlite3, proc busy_handler*(para1: PSqlite3,
para2: proc (para1: pointer, para2: int32): int32{.cdecl.}, para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
para3: pointer): int32{.cdecl, dynlib: Lib, para3: pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_busy_handler".} 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".} importc: "sqlite3_busy_timeout".}
proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray, proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl, nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_get_table".} 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".} importc: "sqlite3_free_table".}
# Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf # 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'; # 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".} importc: "sqlite3_mprintf".}
#function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf'; #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".} 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'; #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".} 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{. 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".} importc: "sqlite3_set_authorizer".}
proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.}, proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
para3: pointer): pointer{.cdecl, dynlib: Lib, para3: pointer): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_trace".} importc: "sqlite3_trace".}
proc progress_handler*(para1: PSqlite3, para2: int32, proc progress_handler*(para1: PSqlite3, para2: int32,
para3: proc (para1: pointer): int32{.cdecl.}, para3: proc (para1: pointer): int32{.cdecl.},
para4: pointer){.cdecl, dynlib: Lib, para4: pointer){.cdecl, dynlib: Lib,
importc: "sqlite3_progress_handler".} importc: "sqlite3_progress_handler".}
proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.}, proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
para3: pointer): pointer{.cdecl, dynlib: Lib, para3: pointer): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_commit_hook".} 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".} 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".} importc: "sqlite3_open16".}
proc errcode*(db: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_errcode".} proc errcode*(db: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_errcode".}
proc errmsg*(para1: PSqlite3): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_errmsg".} 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".} importc: "sqlite3_errmsg16".}
proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt, proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt,
pzTail: ptr cstring): int32{.cdecl, dynlib: Lib, pzTail: ptr cstring): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_prepare".} importc: "sqlite3_prepare".}
proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var Pstmt, proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var Pstmt,
pzTail: ptr cstring): cint {. pzTail: ptr cstring): cint {.
importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.} importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.}
proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt, proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt,
pzTail: var pointer): int32{.cdecl, dynlib: Lib, pzTail: var pointer): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_prepare16".} importc: "sqlite3_prepare16".}
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32, proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib, para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_blob".} 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".} 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".} 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".} 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".} importc: "sqlite3_bind_null".}
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32, proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib, para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text".} importc: "sqlite3_bind_text".}
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32, proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib, para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text16".} importc: "sqlite3_bind_text16".}
#function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value'; #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 #These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT
#It's the c world man ;-) #It's the c world man ;-)
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32, proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
para5: int32): int32{.cdecl, dynlib: Lib, para5: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_blob".} importc: "sqlite3_bind_blob".}
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32, proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
para5: int32): int32{.cdecl, dynlib: Lib, para5: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text".} importc: "sqlite3_bind_text".}
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32, proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
para5: int32): int32{.cdecl, dynlib: Lib, para5: int32): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_bind_text16".} 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".} 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".} 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".} dynlib: Lib, importc: "sqlite3_bind_parameter_index".}
#function sqlite3_clear_bindings(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_clear_bindings'; #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".} 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".} 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".} 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".} 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".} dynlib: Lib, importc: "sqlite3_column_decltype16".}
proc step*(para1: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_step".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} importc: "sqlite3_column_type".}
proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_finalize".} importc: "sqlite3_finalize".}
proc reset*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_reset".} proc reset*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_reset".}
proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32, proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
eTextRep: int32, para5: pointer, eTextRep: int32, para5: pointer,
xFunc: Create_function_func_func, xFunc: Create_function_func_func,
xStep: Create_function_step_func, xStep: Create_function_step_func,
xFinal: Create_function_final_func): int32{.cdecl, xFinal: Create_function_final_func): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_create_function".} dynlib: Lib, importc: "sqlite3_create_function".}
proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32, proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
eTextRep: int32, para5: pointer, eTextRep: int32, para5: pointer,
xFunc: Create_function_func_func, xFunc: Create_function_func_func,
xStep: Create_function_step_func, xStep: Create_function_step_func,
xFinal: Create_function_final_func): int32{.cdecl, xFinal: Create_function_final_func): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_create_function16".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} 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".} importc: "sqlite3_get_auxdata".}
proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer, proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib, para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib,
importc: "sqlite3_set_auxdata".} importc: "sqlite3_set_auxdata".}
proc result_blob*(para1: Pcontext, para2: pointer, para3: int32, proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib, para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_blob".} 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".} 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".} 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".} 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".} 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".} importc: "sqlite3_result_int64".}
proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib, proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib,
importc: "sqlite3_result_null".} importc: "sqlite3_result_null".}
proc result_text*(para1: Pcontext, para2: cstring, para3: int32, proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib, para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text".} importc: "sqlite3_result_text".}
proc result_text16*(para1: Pcontext, para2: pointer, para3: int32, proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib, para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text16".} importc: "sqlite3_result_text16".}
proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32, proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib, para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text16le".} importc: "sqlite3_result_text16le".}
proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32, proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
para4: Result_func){.cdecl, dynlib: Lib, para4: Result_func){.cdecl, dynlib: Lib,
importc: "sqlite3_result_text16be".} 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".} 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{. para4: pointer, xCompare: Create_collation_func): int32{.
cdecl, dynlib: Lib, importc: "sqlite3_create_collation".} 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{. para4: pointer, xCompare: Create_collation_func): int32{.
cdecl, dynlib: Lib, importc: "sqlite3_create_collation16".} cdecl, dynlib: Lib, importc: "sqlite3_create_collation16".}
proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{. 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{. proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
cdecl, dynlib: Lib, importc: "sqlite3_collation_needed16".} cdecl, dynlib: Lib, importc: "sqlite3_collation_needed16".}
proc libversion*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".} 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".} proc version*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
# Not published functions # Not published functions
proc libversion_number*(): int32{.cdecl, dynlib: Lib, proc libversion_number*(): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_libversion_number".} importc: "sqlite3_libversion_number".}
#function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key'; #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'; #function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey';

View file

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