lib: Trim .nim files trailing whitespace
via OSX: find . -name '*.nim' -exec sed -i '' -E 's/[[:space:]]+$//' {} +
This commit is contained in:
parent
d681812465
commit
43bddf62dd
67 changed files with 2435 additions and 2435 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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>".}
|
||||||
|
|
|
||||||
|
|
@ -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>".}
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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."""
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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]")
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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) )
|
||||||
|
|
|
||||||
|
|
@ -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>".}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]=
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)[]
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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] = "..."
|
||||||
|
|
|
||||||
|
|
@ -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>".}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue