mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
lib: Trim .nim files trailing whitespace
via OSX: find . -name '*.nim' -exec sed -i '' -E 's/[[:space:]]+$//' {} +
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## A higher level `mySQL`:idx: database wrapper. The same interface is
|
||||
## A higher level `mySQL`:idx: database wrapper. The same interface is
|
||||
## implemented for other databases too.
|
||||
|
||||
import strutils, mysql
|
||||
@@ -29,22 +29,22 @@ type
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
e.msg = $mysql.error(db)
|
||||
raise e
|
||||
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
## raises an EDb exception with message `msg`.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -55,9 +55,9 @@ when false:
|
||||
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
||||
var stmt = mysql_stmt_init(db)
|
||||
if stmt == nil: dbError(db)
|
||||
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
|
||||
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
|
||||
dbError(db)
|
||||
var
|
||||
var
|
||||
binding: seq[MYSQL_BIND]
|
||||
discard mysql_stmt_close(stmt)
|
||||
|
||||
@@ -79,9 +79,9 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
else:
|
||||
add(result, dbQuote(args[a]))
|
||||
inc(a)
|
||||
else:
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
@@ -97,19 +97,19 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
## executes the query and raises EDB if not successful.
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||
|
||||
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||
if row != nil:
|
||||
while mysql.fetchRow(sqlres) != nil: discard
|
||||
mysql.freeResult(sqlres)
|
||||
|
||||
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For MySQL this is the case!.
|
||||
rawExec(db, query, args)
|
||||
@@ -121,7 +121,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
while true:
|
||||
row = mysql.fetchRow(sqlres)
|
||||
if row == nil: break
|
||||
for i in 0..L-1:
|
||||
for i in 0..L-1:
|
||||
setLen(result[i], 0)
|
||||
if row[i] == nil:
|
||||
result[i] = nil
|
||||
@@ -164,8 +164,8 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||
var L = int(mysql.numFields(sqlres))
|
||||
result = newRow(L)
|
||||
var row = mysql.fetchRow(sqlres)
|
||||
if row != nil:
|
||||
for i in 0..L-1:
|
||||
if row != nil:
|
||||
for i in 0..L-1:
|
||||
setLen(result[i], 0)
|
||||
if row[i] == nil:
|
||||
result[i] = nil
|
||||
@@ -173,7 +173,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||
add(result[i], row[i])
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
@@ -196,44 +196,44 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
inc(j)
|
||||
mysql.freeResult(sqlres)
|
||||
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## same as `fastRows`, but slower and safe.
|
||||
for r in items(getAllRows(db, query, args)): yield r
|
||||
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## executes the query and returns the first column of the first row of the
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
## value is NULL.
|
||||
result = getRow(db, query, args)[0]
|
||||
|
||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32:
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32:
|
||||
result = -1'i64
|
||||
else:
|
||||
result = mysql.insertId(db)
|
||||
|
||||
proc insertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
|
||||
proc insertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## runs the query (typically "UPDATE") and returns the
|
||||
## number of affected rows
|
||||
rawExec(db, query, args)
|
||||
result = mysql.affectedRows(db)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if db != nil: mysql.close(db)
|
||||
|
||||
@@ -242,14 +242,14 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
result = mysql.init(nil)
|
||||
if result == nil: dbError("could not open database connection")
|
||||
if result == nil: dbError("could not open database connection")
|
||||
let
|
||||
colonPos = connection.find(':')
|
||||
host = if colonPos < 0: connection
|
||||
else: substr(connection, 0, colonPos-1)
|
||||
port: int32 = if colonPos < 0: 0'i32
|
||||
else: substr(connection, colonPos+1).parseInt.int32
|
||||
if mysql.realConnect(result, host, user, password, database,
|
||||
if mysql.realConnect(result, host, user, password, database,
|
||||
port, nil, 0) == nil:
|
||||
var errmsg = $mysql.error(result)
|
||||
db_mysql.close(result)
|
||||
@@ -257,6 +257,6 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## A higher level `PostgreSQL`:idx: database wrapper. This interface
|
||||
## A higher level `PostgreSQL`:idx: database wrapper. This interface
|
||||
## is implemented for other databases too.
|
||||
|
||||
import strutils, postgres
|
||||
@@ -20,7 +20,7 @@ type
|
||||
## used to get a row's
|
||||
## column text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
||||
|
||||
@@ -30,15 +30,15 @@ type
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
|
||||
TSqlPrepared: SqlPrepared].}
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
result = SqlQuery(query)
|
||||
|
||||
|
||||
proc dbError*(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
@@ -73,7 +73,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
inc(a)
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
@@ -106,7 +106,7 @@ proc exec*(db: DbConn, stmtName: SqlPrepared,
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): PPGresult =
|
||||
var arr = allocCStringArray(args)
|
||||
@@ -128,7 +128,7 @@ proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
|
||||
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
|
||||
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
return SqlPrepared(stmtName)
|
||||
|
||||
|
||||
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||
for col in 0..cols-1:
|
||||
setLen(r[col], 0)
|
||||
@@ -140,7 +140,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Postgres it is safe though.
|
||||
var res = setupQuery(db, query, args)
|
||||
@@ -224,14 +224,14 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||
## value is NULL.
|
||||
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
||||
result = if isNil(x): "" else: $x
|
||||
|
||||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
||||
## named ``id``.
|
||||
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
||||
args), 0, 0)
|
||||
if not isNil(x):
|
||||
result = parseBiggestInt($x)
|
||||
@@ -240,13 +240,13 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
## named ``id``.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [
|
||||
FReadDB, FWriteDb].} =
|
||||
@@ -286,6 +286,6 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## A higher level `SQLite`:idx: database wrapper. This interface
|
||||
## A higher level `SQLite`:idx: database wrapper. This interface
|
||||
## is implemented for other databases too.
|
||||
|
||||
import strutils, sqlite3
|
||||
@@ -19,31 +19,31 @@ type
|
||||
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
||||
## text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
e.msg = $sqlite3.errmsg(db)
|
||||
raise e
|
||||
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
## raises an EDb exception with message `msg`.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -67,8 +67,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
inc(a)
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
@@ -81,32 +81,32 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
if not tryExec(db, query, args): dbError(db)
|
||||
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): Pstmt =
|
||||
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): Pstmt =
|
||||
var q = dbFormat(query, args)
|
||||
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
|
||||
|
||||
|
||||
proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
||||
for col in 0..cols-1:
|
||||
setLen(r[col], column_bytes(stmt, col)) # set capacity
|
||||
setLen(r[col], 0)
|
||||
let x = column_text(stmt, col)
|
||||
if not isNil(x): add(r[col], x)
|
||||
|
||||
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Sqlite it is safe though.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (column_count(stmt))
|
||||
var result = newRow(L)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
setRow(stmt, result, L)
|
||||
yield result
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
@@ -136,31 +136,31 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (column_count(stmt))
|
||||
result = newRow(L)
|
||||
if step(stmt) == SQLITE_ROW:
|
||||
if step(stmt) == SQLITE_ROW:
|
||||
setRow(stmt, result, L)
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in fastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||
## same as `FastRows`, but slower and safe.
|
||||
for r in fastRows(db, query, args): yield r
|
||||
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
||||
## executes the query and returns the first column of the first row of the
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
## value is NULL.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
if step(stmt) == SQLITE_ROW:
|
||||
let cb = column_bytes(stmt, 0)
|
||||
if cb == 0:
|
||||
if cb == 0:
|
||||
result = ""
|
||||
else:
|
||||
result = newStringOfCap(cb)
|
||||
@@ -168,12 +168,12 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||
else:
|
||||
result = ""
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64
|
||||
{.tags: [FWriteDb], raises: [].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.Pstmt
|
||||
result = -1
|
||||
@@ -183,27 +183,27 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
if finalize(stmt) != SQLITE_OK:
|
||||
result = -1
|
||||
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
## named ``id``.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
exec(db, query, args)
|
||||
result = changes(db)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||
|
||||
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
@@ -216,12 +216,12 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
##
|
||||
## Note that the encoding cannot be changed once it's been set.
|
||||
## According to SQLite3 documentation, any attempt to change
|
||||
## the encoding after the database is created will be silently
|
||||
## According to SQLite3 documentation, any attempt to change
|
||||
## the encoding after the database is created will be silently
|
||||
## ignored.
|
||||
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
||||
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## This module implements graphical output for Nim; the current
|
||||
## implementation uses SDL but the interface is meant to support multiple
|
||||
## backends some day. There is no need to init SDL as this module does that
|
||||
## backends some day. There is no need to init SDL as this module does that
|
||||
## implicitly.
|
||||
|
||||
import colors, math
|
||||
@@ -24,7 +24,7 @@ type
|
||||
Surface* {.pure, final.} = object
|
||||
w*, h*: Natural
|
||||
s*: sdl.PSurface
|
||||
|
||||
|
||||
EGraphics* = object of IOError
|
||||
|
||||
Font {.pure, final.} = object
|
||||
@@ -35,7 +35,7 @@ type
|
||||
|
||||
proc toSdlColor*(c: Color): sdl.Color =
|
||||
## Convert colors.Color to sdl.Color
|
||||
var x = c.extractRGB
|
||||
var x = c.extractRGB
|
||||
result.r = x.r and 0xff
|
||||
result.g = x.g and 0xff
|
||||
result.b = x.b and 0xff
|
||||
@@ -43,7 +43,7 @@ proc toSdlColor*(c: Color): sdl.Color =
|
||||
proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
|
||||
## Creates a color using ``sdl.MapRGBA``.
|
||||
var x = c.extractRGB
|
||||
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
|
||||
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
|
||||
x.b and 0xff, alpha and 0xff)
|
||||
|
||||
proc toSdlRect*(r: Rect): sdl.Rect =
|
||||
@@ -53,26 +53,26 @@ proc toSdlRect*(r: Rect): sdl.Rect =
|
||||
result.w = uint16(r.width)
|
||||
result.h = uint16(r.height)
|
||||
|
||||
proc raiseEGraphics =
|
||||
proc raiseEGraphics =
|
||||
raise newException(EGraphics, $sdl.getError())
|
||||
|
||||
|
||||
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
|
||||
|
||||
|
||||
proc newSurface*(width, height: int): PSurface =
|
||||
## creates a new surface.
|
||||
new(result, surfaceFinalizer)
|
||||
result.w = width
|
||||
result.h = height
|
||||
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
|
||||
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
|
||||
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
|
||||
if result.s == nil:
|
||||
raiseEGraphics()
|
||||
|
||||
|
||||
assert(not sdl.mustLock(result.s))
|
||||
|
||||
proc fontFinalizer(f: PFont) = closeFont(f.f)
|
||||
|
||||
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
|
||||
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
|
||||
## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
|
||||
new(result, fontFinalizer)
|
||||
result.f = openFont(name, size.cint)
|
||||
@@ -84,7 +84,7 @@ var
|
||||
defaultFont*: PFont ## default font that is used; this needs to initialized
|
||||
## by the client!
|
||||
|
||||
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
|
||||
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
|
||||
## initializes the `defaultFont` var.
|
||||
defaultFont = newFont(name, size, color)
|
||||
|
||||
@@ -98,7 +98,7 @@ proc newScreenSurface*(width, height: int): PSurface =
|
||||
raiseEGraphics()
|
||||
|
||||
proc writeToBMP*(sur: PSurface, filename: string) =
|
||||
## Saves the contents of the surface `sur` to the file `filename` as a
|
||||
## Saves the contents of the surface `sur` to the file `filename` as a
|
||||
## BMP file.
|
||||
if sdl.saveBMP(sur.s, filename) != 0:
|
||||
raise newException(IOError, "cannot write: " & filename)
|
||||
@@ -111,7 +111,7 @@ type
|
||||
template setPix(video, pitch, x, y, col: expr): stmt =
|
||||
video[y * pitch + x] = int32(col)
|
||||
|
||||
template getPix(video, pitch, x, y: expr): expr =
|
||||
template getPix(video, pitch, x, y: expr): expr =
|
||||
colors.Color(video[y * pitch + x])
|
||||
|
||||
const
|
||||
@@ -120,7 +120,7 @@ const
|
||||
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
|
||||
assert x <% sur.w
|
||||
assert y <% sur.h
|
||||
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
|
||||
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
|
||||
x, y)
|
||||
|
||||
proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
|
||||
@@ -146,7 +146,7 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
|
||||
## set the pixel at position ``(x, y)``. No range checking is done!
|
||||
setPixel(sur, x, y, col)
|
||||
|
||||
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
||||
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
||||
srcRect: Rect) =
|
||||
## Copies ``srcSurf`` into ``destSurf``
|
||||
var destTRect, srcTRect: sdl.Rect
|
||||
@@ -175,7 +175,7 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
|
||||
## font.
|
||||
var textSur: PSurface # This surface will have the text drawn on it
|
||||
new(textSur, surfaceFinalizer)
|
||||
|
||||
|
||||
# Render the text
|
||||
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
|
||||
# Merge the text surface with sur
|
||||
@@ -183,14 +183,14 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
|
||||
|
||||
proc drawText*(sur: PSurface, p: Point, text: string,
|
||||
bg: Color, font = defaultFont) =
|
||||
## Draws text, at location ``p`` with font ``font``. ``bg``
|
||||
## Draws text, at location ``p`` with font ``font``. ``bg``
|
||||
## is the background color.
|
||||
var textSur: PSurface # This surface will have the text drawn on it
|
||||
new(textSur, surfaceFinalizer)
|
||||
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
|
||||
# Merge the text surface with sur
|
||||
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
|
||||
|
||||
|
||||
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
## draws a circle with center `p` and radius `r` with the given color
|
||||
## onto the surface `sur`.
|
||||
@@ -205,7 +205,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
if x+px <% sur.w:
|
||||
if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color)
|
||||
if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color)
|
||||
|
||||
|
||||
if x-px <% sur.w:
|
||||
if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color)
|
||||
if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color)
|
||||
@@ -213,7 +213,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
if x+py <% sur.w:
|
||||
if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color)
|
||||
if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color)
|
||||
|
||||
|
||||
if x-py <% sur.w:
|
||||
if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color)
|
||||
if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color)
|
||||
@@ -225,10 +225,10 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
py = py - 1
|
||||
px = px + 1
|
||||
|
||||
proc `>-<`(val: int, s: PSurface): int {.inline.} =
|
||||
proc `>-<`(val: int, s: PSurface): int {.inline.} =
|
||||
return if val < 0: 0 elif val >= s.w: s.w-1 else: val
|
||||
|
||||
proc `>|<`(val: int, s: PSurface): int {.inline.} =
|
||||
proc `>|<`(val: int, s: PSurface): int {.inline.} =
|
||||
return if val < 0: 0 elif val >= s.h: s.h-1 else: val
|
||||
|
||||
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
@@ -242,7 +242,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
var dy = y1 - y0
|
||||
var dx = x1 - x0
|
||||
if dy < 0:
|
||||
dy = -dy
|
||||
dy = -dy
|
||||
stepy = -1
|
||||
else:
|
||||
stepy = 1
|
||||
@@ -251,7 +251,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
stepx = -1
|
||||
else:
|
||||
stepx = 1
|
||||
dy = dy * 2
|
||||
dy = dy * 2
|
||||
dx = dx * 2
|
||||
var video = cast[PPixels](sur.s.pixels)
|
||||
var pitch = sur.s.pitch.int div ColSize
|
||||
@@ -328,17 +328,17 @@ proc drawRect*(sur: PSurface, r: Rect, color: Color) =
|
||||
if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h):
|
||||
var minW = min(sur.s.w - r.x, r.width)
|
||||
var minH = min(sur.s.h - r.y, r.height)
|
||||
|
||||
|
||||
# Draw Top
|
||||
for i in 0 .. minW - 1:
|
||||
setPix(video, pitch, r.x + i, r.y, color)
|
||||
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
|
||||
|
||||
|
||||
# Draw left side
|
||||
for i in 0 .. minH - 1:
|
||||
setPix(video, pitch, r.x, r.y + i, color)
|
||||
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
|
||||
|
||||
|
||||
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
|
||||
## Fills a rectangle using sdl's ``FillRect`` function.
|
||||
var rect = toSdlRect(r)
|
||||
@@ -350,23 +350,23 @@ proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) =
|
||||
var pitch = sur.s.pitch.int div ColSize
|
||||
if cx+x <= sur.s.w-1:
|
||||
if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col)
|
||||
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
|
||||
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
|
||||
if cx-x <= sur.s.w-1:
|
||||
if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col)
|
||||
if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col)
|
||||
|
||||
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
col: Color) =
|
||||
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
|
||||
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
|
||||
## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
|
||||
## of the ellipse.
|
||||
var
|
||||
var
|
||||
x, y: Natural
|
||||
xChange, yChange: int
|
||||
ellipseError: Natural
|
||||
twoASquare, twoBSquare: Natural
|
||||
stoppingX, stoppingY: Natural
|
||||
|
||||
|
||||
twoASquare = 2 * xRadius * xRadius
|
||||
twoBSquare = 2 * yRadius * yRadius
|
||||
x = xRadius
|
||||
@@ -376,7 +376,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
ellipseError = 0
|
||||
stoppingX = twoBSquare * xRadius
|
||||
stoppingY = 0
|
||||
|
||||
|
||||
while stoppingX >= stoppingY: # 1st set of points, y` > - 1
|
||||
sur.plot4EllipsePoints(cx, cy, x, y, col)
|
||||
inc(y)
|
||||
@@ -388,7 +388,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
dec(stoppingX, twoBSquare)
|
||||
inc(ellipseError, xChange)
|
||||
inc(xChange, twoBSquare)
|
||||
|
||||
|
||||
# 1st point set is done; start the 2nd set of points
|
||||
x = 0
|
||||
y = yRadius
|
||||
@@ -408,7 +408,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
dec(stoppingY, twoASquare)
|
||||
inc(ellipseError, yChange)
|
||||
inc(yChange,twoASquare)
|
||||
|
||||
|
||||
|
||||
proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
|
||||
if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h):
|
||||
@@ -419,43 +419,43 @@ proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
|
||||
|
||||
setPix(video, pitch, x, y,
|
||||
pixColor.intensity(1.0 - c) + color.intensity(c))
|
||||
|
||||
|
||||
template ipart(x: expr): expr = floor(x)
|
||||
|
||||
template ipart(x: expr): expr = floor(x)
|
||||
template cround(x: expr): expr = ipart(x + 0.5)
|
||||
template fpart(x: expr): expr = x - ipart(x)
|
||||
template rfpart(x: expr): expr = 1.0 - fpart(x)
|
||||
|
||||
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
|
||||
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
|
||||
## line algorithm
|
||||
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
|
||||
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
|
||||
p1.y.toFloat(), p2.y.toFloat())
|
||||
var dx = x2 - x1
|
||||
var dy = y2 - y1
|
||||
|
||||
|
||||
var ax = dx
|
||||
if ax < 0'f64:
|
||||
ax = 0'f64 - ax
|
||||
var ay = dy
|
||||
if ay < 0'f64:
|
||||
ay = 0'f64 - ay
|
||||
|
||||
|
||||
if ax < ay:
|
||||
swap(x1, y1)
|
||||
swap(x2, y2)
|
||||
swap(dx, dy)
|
||||
|
||||
|
||||
template doPlot(x, y: int, c: float, color: Color): stmt =
|
||||
if ax < ay:
|
||||
sur.plotAA(y, x, c, color)
|
||||
else:
|
||||
sur.plotAA(x, y, c, color)
|
||||
|
||||
|
||||
if x2 < x1:
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
|
||||
|
||||
var gradient = dy / dx
|
||||
# handle first endpoint
|
||||
var xend = cround(x1)
|
||||
@@ -509,19 +509,19 @@ when not defined(testing) and isMainModule:
|
||||
# Draw the shapes
|
||||
surf.drawLineAA((150, 170), (400, 471), colTan)
|
||||
surf.drawLine((100, 170), (400, 471), colRed)
|
||||
|
||||
|
||||
surf.drawEllipse(200, 300, 200, 30, colSeaGreen)
|
||||
surf.drawHorLine(1, 300, 400, colViolet)
|
||||
surf.drawHorLine(1, 300, 400, colViolet)
|
||||
# Check if the ellipse is the size it's suppose to be.
|
||||
surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
|
||||
|
||||
|
||||
surf.drawEllipse(400, 300, 300, 300, colOrange)
|
||||
surf.drawEllipse(5, 5, 5, 5, colGreen)
|
||||
|
||||
|
||||
surf.drawHorLine(5, 5, 900, colRed)
|
||||
surf.drawVerLine(5, 60, 800, colRed)
|
||||
surf.drawCircle((600, 500), 60, colRed)
|
||||
|
||||
|
||||
surf.fillRect((50, 50, 100, 100), colFuchsia)
|
||||
surf.fillRect((150, 50, 100, 100), colGreen)
|
||||
surf.drawRect((50, 150, 100, 100), colGreen)
|
||||
@@ -530,12 +530,12 @@ when not defined(testing) and isMainModule:
|
||||
surf.drawHorLine(250, 150, 100, colRed)
|
||||
|
||||
surf.drawLineAA((592, 160), (592, 280), colPurple)
|
||||
|
||||
|
||||
#surf.drawText((300, 300), "TEST", colMidnightBlue)
|
||||
#var textSize = textBounds("TEST")
|
||||
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
|
||||
# $textSize.height, colDarkGreen)
|
||||
|
||||
|
||||
var mouseStartX = -1
|
||||
var mouseStartY = -1
|
||||
withEvents(surf, event):
|
||||
@@ -561,17 +561,17 @@ when not defined(testing) and isMainModule:
|
||||
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
|
||||
mouseStartX = -1
|
||||
mouseStartY = -1
|
||||
|
||||
|
||||
of sdl.MOUSEMOTION:
|
||||
var mm = sdl.evMouseMotion(eventp)
|
||||
if mouseStartX != -1 and mouseStartY != -1:
|
||||
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
|
||||
#echo(mm.x, " ", mm.y, " ", mm.yrel)
|
||||
|
||||
|
||||
else:
|
||||
discard "echo(event.kind)"
|
||||
|
||||
|
||||
sdl.updateRect(surf.s, 0, 0, 800, 600)
|
||||
|
||||
|
||||
surf.writeToBMP("test.bmp")
|
||||
sdl.quit()
|
||||
|
||||
@@ -41,11 +41,11 @@ type
|
||||
reExtended = 3, ## ignore whitespace and ``#`` comments
|
||||
reStudy = 4 ## study the expression (may be omitted if the
|
||||
## expression will be used only once)
|
||||
|
||||
RegexDesc = object
|
||||
|
||||
RegexDesc = object
|
||||
h: ptr Pcre
|
||||
e: ptr ExtraData
|
||||
|
||||
|
||||
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
|
||||
|
||||
RegexError* = object of ValueError
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides an easy to use sockets-style
|
||||
## This module provides an easy to use sockets-style
|
||||
## nim interface to the OpenSSL library.
|
||||
|
||||
{.deprecated.}
|
||||
@@ -20,37 +20,37 @@ type
|
||||
bio: BIO
|
||||
{.deprecated: [TSecureSocket: SecureSocket].}
|
||||
|
||||
proc connect*(sock: var SecureSocket, address: string,
|
||||
proc connect*(sock: var SecureSocket, address: string,
|
||||
port: int): int =
|
||||
## Connects to the specified `address` on the specified `port`.
|
||||
## Returns the result of the certificate validation.
|
||||
SslLoadErrorStrings()
|
||||
ERR_load_BIO_strings()
|
||||
|
||||
|
||||
if SSL_library_init() != 1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
var ctx = SSL_CTX_new(SSLv23_client_method())
|
||||
if ctx == nil:
|
||||
ERR_print_errors_fp(stderr)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
#if SSL_CTX_load_verify_locations(ctx,
|
||||
|
||||
#if SSL_CTX_load_verify_locations(ctx,
|
||||
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
|
||||
# echo("Failed load verify locations")
|
||||
# ERR_print_errors_fp(stderr)
|
||||
|
||||
|
||||
sock.bio = BIO_new_ssl_connect(ctx)
|
||||
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
if BIO_do_connect(sock.bio) <= 0:
|
||||
ERR_print_errors_fp(stderr)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
result = SSL_get_verify_result(sock.ssl)
|
||||
|
||||
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
||||
@@ -86,12 +86,12 @@ proc close*(sock: SecureSocket) =
|
||||
when not defined(testing) and isMainModule:
|
||||
var s: SecureSocket
|
||||
echo connect(s, "smtp.gmail.com", 465)
|
||||
|
||||
|
||||
#var buffer: array[0..255, char]
|
||||
#echo BIO_read(bio, buffer, buffer.len)
|
||||
var buffer: string = ""
|
||||
|
||||
|
||||
echo s.recvLine(buffer)
|
||||
echo buffer
|
||||
echo buffer
|
||||
echo buffer.len
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Main file to generate a DLL from the standard library.
|
||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||
## Main file to generate a DLL from the standard library.
|
||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||
## too:
|
||||
##
|
||||
## * parseutils
|
||||
@@ -22,12 +22,12 @@
|
||||
## * unicode
|
||||
## * pegs
|
||||
## * ropes
|
||||
##
|
||||
##
|
||||
|
||||
when system.appType != "lib":
|
||||
{.error: "This file has to be compiled as a library!".}
|
||||
|
||||
when not defined(createNimRtl):
|
||||
when not defined(createNimRtl):
|
||||
{.error: "This file has to be compiled with '-d:createNimRtl'".}
|
||||
|
||||
import
|
||||
|
||||
@@ -29,34 +29,34 @@ const
|
||||
|
||||
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
|
||||
|
||||
const
|
||||
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
|
||||
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
|
||||
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
|
||||
const
|
||||
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
|
||||
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
|
||||
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
|
||||
|
||||
type
|
||||
epoll_data* {.importc: "union epoll_data",
|
||||
type
|
||||
epoll_data* {.importc: "union epoll_data",
|
||||
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
||||
#thePtr* {.importc: "ptr".}: pointer
|
||||
fd* {.importc: "fd".}: cint # \
|
||||
#u32*: uint32
|
||||
#u64*: uint64
|
||||
|
||||
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
|
||||
events*: uint32 # Epoll events
|
||||
data*: epoll_data # User data variable
|
||||
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
|
||||
events*: uint32 # Epoll events
|
||||
data*: epoll_data # User data variable
|
||||
|
||||
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
|
||||
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
|
||||
header: "<sys/epoll.h>".}
|
||||
## Creates an epoll instance. Returns an fd for the new instance.
|
||||
## The "size" parameter is a hint specifying the number of file
|
||||
## descriptors to be associated with the new instance. The fd
|
||||
## returned by epoll_create() should be closed with close().
|
||||
## returned by epoll_create() should be closed with close().
|
||||
|
||||
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
|
||||
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
|
||||
header: "<sys/epoll.h>".}
|
||||
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
||||
## parameter has been dropped.
|
||||
## parameter has been dropped.
|
||||
|
||||
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
|
||||
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
||||
@@ -65,10 +65,10 @@ proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_
|
||||
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
|
||||
## constants defined above. The "fd" parameter is the target of the
|
||||
## operation. The "event" parameter describes which events the caller
|
||||
## is interested in and any associated user data.
|
||||
## is interested in and any associated user data.
|
||||
|
||||
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
timeout: cint): cint {.importc: "epoll_wait",
|
||||
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
timeout: cint): cint {.importc: "epoll_wait",
|
||||
header: "<sys/epoll.h>".}
|
||||
## Wait for events on an epoll instance "epfd". Returns the number of
|
||||
## triggered events returned in "events" buffer. Or -1 in case of
|
||||
@@ -82,11 +82,11 @@ proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
## __THROW.
|
||||
|
||||
|
||||
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
# timeout: cint; ss: ptr sigset_t): cint {.
|
||||
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
|
||||
# Same as epoll_wait, but the thread's signal mask is temporarily
|
||||
# and atomically replaced with the one provided as parameter.
|
||||
#
|
||||
# This function is a cancellation point and therefore not marked with
|
||||
# __THROW.
|
||||
# __THROW.
|
||||
|
||||
@@ -9,65 +9,65 @@
|
||||
|
||||
{.deadCodeElim:on.}
|
||||
|
||||
# Get the platform-dependent flags.
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object
|
||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||
name*{.importc: "name".}: char # Name.
|
||||
# Get the platform-dependent flags.
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object
|
||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||
name*{.importc: "name".}: char # Name.
|
||||
{.deprecated: [Tinotify_event: InotifyEvent].}
|
||||
|
||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||
const
|
||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||
IN_MODIFY* = 0x00000002 # File was modified.
|
||||
IN_ATTRIB* = 0x00000004 # Metadata changed.
|
||||
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
|
||||
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
|
||||
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
|
||||
IN_OPEN* = 0x00000020 # File was opened.
|
||||
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
|
||||
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
|
||||
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
|
||||
IN_CREATE* = 0x00000100 # Subfile was created.
|
||||
IN_DELETE* = 0x00000200 # Subfile was deleted.
|
||||
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
|
||||
IN_MOVE_SELF* = 0x00000800 # Self was moved.
|
||||
# Events sent by the kernel.
|
||||
const
|
||||
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
|
||||
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
|
||||
IN_IGNORED* = 0x00008000 # File was ignored.
|
||||
# Special flags.
|
||||
const
|
||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||
const
|
||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||
IN_MODIFY* = 0x00000002 # File was modified.
|
||||
IN_ATTRIB* = 0x00000004 # Metadata changed.
|
||||
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
|
||||
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
|
||||
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
|
||||
IN_OPEN* = 0x00000020 # File was opened.
|
||||
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
|
||||
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
|
||||
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
|
||||
IN_CREATE* = 0x00000100 # Subfile was created.
|
||||
IN_DELETE* = 0x00000200 # Subfile was deleted.
|
||||
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
|
||||
IN_MOVE_SELF* = 0x00000800 # Self was moved.
|
||||
# Events sent by the kernel.
|
||||
const
|
||||
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
|
||||
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
|
||||
IN_IGNORED* = 0x00008000 # File was ignored.
|
||||
# Special flags.
|
||||
const
|
||||
IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a
|
||||
# directory.
|
||||
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
|
||||
# directory.
|
||||
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
|
||||
IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
|
||||
# objects.
|
||||
# objects.
|
||||
IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
|
||||
# existing watch.
|
||||
IN_ISDIR* = 0x40000000 # Event occurred against dir.
|
||||
IN_ONESHOT* = 0x80000000 # Only send event once.
|
||||
# All events which a program can wait on.
|
||||
const
|
||||
# existing watch.
|
||||
IN_ISDIR* = 0x40000000 # Event occurred against dir.
|
||||
IN_ONESHOT* = 0x80000000 # Only send event once.
|
||||
# All events which a program can wait on.
|
||||
const
|
||||
IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or
|
||||
IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or
|
||||
IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
|
||||
# Create and initialize inotify instance.
|
||||
proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
|
||||
proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
|
||||
header: "<sys/inotify.h>".}
|
||||
# Create and initialize inotify instance.
|
||||
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
|
||||
# Create and initialize inotify instance.
|
||||
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
|
||||
header: "<sys/inotify.h>".}
|
||||
# Add watch of object NAME to inotify instance FD. Notify about
|
||||
# events specified by MASK.
|
||||
# events specified by MASK.
|
||||
proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
|
||||
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
|
||||
# Remove the watch specified by WD from the inotify instance FD.
|
||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||
# Remove the watch specified by WD from the inotify instance FD.
|
||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
|
||||
|
||||
@@ -24,5 +24,5 @@ const
|
||||
|
||||
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
||||
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
||||
arg: pointer; ptid: ptr Pid; tls: pointer;
|
||||
arg: pointer; ptid: ptr Pid; tls: pointer;
|
||||
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
|
||||
|
||||
@@ -41,7 +41,7 @@ type
|
||||
Actor[In, Out] = object{.pure, final.}
|
||||
i: Channel[Task[In, Out]]
|
||||
t: TThread[ptr Actor[In, Out]]
|
||||
|
||||
|
||||
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
|
||||
{.deprecated: [TTask: Task, TActor: Actor].}
|
||||
|
||||
@@ -83,7 +83,7 @@ proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
|
||||
shallowCopy(t.data, msg)
|
||||
send(receiver.i, t)
|
||||
|
||||
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
||||
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
||||
sender: ptr Channel[Out] = nil) =
|
||||
## sends a message to `receiver`'s inbox.
|
||||
var t: Task[In, Out]
|
||||
@@ -138,7 +138,7 @@ proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
|
||||
|
||||
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
||||
## waits for every actor of `a` to finish with its work. Currently this is
|
||||
## implemented as polling every `polling` ms and has a slight chance
|
||||
## implemented as polling every `polling` ms and has a slight chance
|
||||
## of failing since we check for every actor to be in `ready` state and not
|
||||
## for messages still in ether. This will change in a later
|
||||
## version, however.
|
||||
@@ -146,7 +146,7 @@ proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
||||
while true:
|
||||
var wait = false
|
||||
for i in 0..high(a.actors):
|
||||
if not a.actors[i].i.ready:
|
||||
if not a.actors[i].i.ready:
|
||||
wait = true
|
||||
allReadyCount = 0
|
||||
break
|
||||
@@ -222,7 +222,7 @@ proc spawn*[In](p: var ActorPool[In, void], input: In,
|
||||
var t: Task[In, void]
|
||||
setupTask()
|
||||
schedule()
|
||||
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var
|
||||
a: ActorPool[int, void]
|
||||
|
||||
@@ -195,10 +195,10 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
readBuffer.setLen(res)
|
||||
f.offset.inc(res)
|
||||
retFuture.complete(readBuffer)
|
||||
|
||||
|
||||
if not cb(f.fd):
|
||||
addRead(f.fd, cb)
|
||||
|
||||
|
||||
return retFuture
|
||||
|
||||
proc readLine*(f: AsyncFile): Future[string] {.async.} =
|
||||
@@ -222,7 +222,7 @@ proc getFilePos*(f: AsyncFile): int64 =
|
||||
|
||||
proc setFilePos*(f: AsyncFile, pos: int64) =
|
||||
## Sets the position of the file pointer that is used for read/write
|
||||
## operations. The file's first byte has the index zero.
|
||||
## operations. The file's first byte has the index zero.
|
||||
f.offset = pos
|
||||
when not defined(windows) and not defined(nimdoc):
|
||||
let ret = lseek(f.fd.cint, pos, SEEK_SET)
|
||||
@@ -291,7 +291,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
retFuture.complete()
|
||||
else:
|
||||
var written = 0
|
||||
|
||||
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
result = true
|
||||
let remainderSize = data.len-written
|
||||
@@ -309,7 +309,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
result = false # We still have data to write.
|
||||
else:
|
||||
retFuture.complete()
|
||||
|
||||
|
||||
if not cb(f.fd):
|
||||
addWrite(f.fd, cb)
|
||||
return retFuture
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
## This module implements a base64 encoder and decoder.
|
||||
|
||||
const
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
var numLines = (total + lineLen - 1) div lineLen
|
||||
@@ -29,13 +29,13 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||
var c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
if currLine >= lineLen and i != s.len-2:
|
||||
for x in items(newLine):
|
||||
if currLine >= lineLen and i != s.len-2:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
@@ -45,7 +45,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||
var b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
@@ -61,17 +61,17 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||
#assert(r == result.len)
|
||||
discard
|
||||
|
||||
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
@@ -79,7 +79,7 @@ proc decodeByte(b: char): int {.inline.} =
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
|
||||
proc decode*(s: string): string =
|
||||
proc decode*(s: string): string =
|
||||
## decodes a string in base64 representation back into its original form.
|
||||
## Whitespace is skipped.
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
@@ -96,7 +96,7 @@ proc decode*(s: string): string =
|
||||
var b = s[i+1].decodeByte
|
||||
var c = s[i+2].decodeByte
|
||||
var d = s[i+3].decodeByte
|
||||
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
@@ -105,19 +105,19 @@ proc decode*(s: string): string =
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
|
||||
@@ -13,26 +13,26 @@ import strutils
|
||||
|
||||
## Basic 2d support with vectors, points, matrices and some basic utilities.
|
||||
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
||||
## the translation part of matrix is ignored.
|
||||
## the translation part of matrix is ignored.
|
||||
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars.
|
||||
##
|
||||
## Quick start example:
|
||||
##
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
##
|
||||
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
##
|
||||
##
|
||||
## # Create a 2d point at (100,0) and a vector (5,2)
|
||||
##
|
||||
## var pt:Point2d=point2d(100.0,0.0)
|
||||
##
|
||||
##
|
||||
## var pt:Point2d=point2d(100.0,0.0)
|
||||
##
|
||||
## var vec:Vector2d=vector2d(5.0,2.0)
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
##
|
||||
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
##
|
||||
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
@@ -64,12 +64,12 @@ type
|
||||
## not used for geometric transformations in 2d.
|
||||
ax*,ay*,bx*,by*,tx*,ty*:float
|
||||
Point2d* = object
|
||||
## Implements a non-homogeneous 2d point stored as
|
||||
## Implements a non-homogeneous 2d point stored as
|
||||
## an `x` coordinate and an `y` coordinate.
|
||||
x*,y*:float
|
||||
Vector2d* = object
|
||||
## Implements a 2d **direction vector** stored as
|
||||
## an `x` coordinate and an `y` coordinate. Direction vector means,
|
||||
Vector2d* = object
|
||||
## Implements a 2d **direction vector** stored as
|
||||
## an `x` coordinate and an `y` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*:float
|
||||
@@ -78,7 +78,7 @@ type
|
||||
|
||||
# Some forward declarations...
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
|
||||
## Creates a new matrix.
|
||||
## Creates a new matrix.
|
||||
## `ax`,`ay` is the local x axis
|
||||
## `bx`,`by` is the local y axis
|
||||
## `tx`,`ty` is the translation
|
||||
@@ -99,7 +99,7 @@ let
|
||||
YAXIS*:Vector2d=vector2d(0.0,1.0)
|
||||
## Quick acces to an 2d y-axis unit vector
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Private utils
|
||||
# ***************************************
|
||||
@@ -114,13 +114,13 @@ proc safeArccos(v:float):float=
|
||||
return arccos(clamp(v,-1.0,1.0))
|
||||
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
template makeBinOpVector(s:expr)=
|
||||
## implements binary operators + , - , * and / for vectors
|
||||
proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||
proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
@@ -144,7 +144,7 @@ proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
|
||||
|
||||
proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
|
||||
## Concatenates matrices returning a new matrix.
|
||||
|
||||
|
||||
# | a.AX a.AY 0 | | b.AX b.AY 0 |
|
||||
# | a.BX a.BY 0 | * | b.BX b.BY 0 |
|
||||
# | a.TX a.TY 1 | | b.TX b.TY 1 |
|
||||
@@ -153,7 +153,7 @@ proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
|
||||
a.ax * b.ay + a.ay * b.by,
|
||||
a.bx * b.ax + a.by * b.bx,
|
||||
a.bx * b.ay + a.by * b.by,
|
||||
a.tx * b.ax + a.ty * b.bx + b.tx,
|
||||
a.tx * b.ax + a.ty * b.bx + b.tx,
|
||||
a.tx * b.ay + a.ty * b.by + b.ty)
|
||||
|
||||
|
||||
@@ -169,13 +169,13 @@ proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
result.setElements(sx,0,0,sy,0,0)
|
||||
|
||||
|
||||
proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
|
||||
|
||||
|
||||
proc move*(dx,dy:float):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,1,dx,dy)
|
||||
@@ -187,7 +187,7 @@ proc move*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
proc rotate*(rad:float):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians
|
||||
let
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,0,0)
|
||||
@@ -200,7 +200,7 @@ proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
|
||||
|
||||
|
||||
proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through origo and
|
||||
@@ -211,7 +211,7 @@ proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=v.x*v.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
|
||||
|
||||
@@ -230,7 +230,7 @@ proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=v.x*v.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
|
||||
|
||||
@@ -238,47 +238,47 @@ proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
|
||||
sqd,xy2,
|
||||
xy2,-sqd,
|
||||
org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd)
|
||||
|
||||
|
||||
|
||||
|
||||
proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
|
||||
## Returns a new skew matrix, which has its
|
||||
## Returns a new skew matrix, which has its
|
||||
## x axis rotated `xskew` radians from the local x axis, and
|
||||
## y axis rotated `yskew` radians from the local y axis
|
||||
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
|
||||
|
||||
|
||||
|
||||
proc `$`* (t:Matrix2d):string {.noInit.} =
|
||||
## Returns a string representation of the matrix
|
||||
return rtos(t.ax) & "," & rtos(t.ay) &
|
||||
"," & rtos(t.bx) & "," & rtos(t.by) &
|
||||
"," & rtos(t.bx) & "," & rtos(t.by) &
|
||||
"," & rtos(t.tx) & "," & rtos(t.ty)
|
||||
|
||||
proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a circle into an ellipse.
|
||||
## `tol` is used as tolerance for both equal length comparison
|
||||
## `tol` is used as tolerance for both equal length comparison
|
||||
## and perp. comparison.
|
||||
|
||||
|
||||
#dot product=0 means perpendicular coord. system:
|
||||
if abs(t.ax*t.bx+t.ay*t.by)<=tol:
|
||||
if abs(t.ax*t.bx+t.ay*t.by)<=tol:
|
||||
#subtract squared lengths of axes to check if uniform scaling:
|
||||
if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
proc determinant*(t:Matrix2d):float=
|
||||
## Computes the determinant of the matrix.
|
||||
|
||||
|
||||
#NOTE: equivalent with perp.dot product for two 2d vectors
|
||||
return t.ax*t.by-t.bx*t.ay
|
||||
return t.ax*t.by-t.bx*t.ay
|
||||
|
||||
proc isMirroring* (m:Matrix2d):bool=
|
||||
## Checks if the `m` is a mirroring matrix,
|
||||
## which means it will reverse direction of a curve transformed with it
|
||||
return m.determinant<0.0
|
||||
|
||||
|
||||
proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
## Returns a new matrix, which is the inverse of the matrix
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
@@ -286,7 +286,7 @@ proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
let d=m.determinant
|
||||
if d==0.0:
|
||||
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
|
||||
|
||||
|
||||
result.setElements(
|
||||
m.by/d,-m.ay/d,
|
||||
-m.bx/d,m.ax/d,
|
||||
@@ -296,14 +296,14 @@ proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
return
|
||||
abs(m1.ax-m2.ax)<=tol and
|
||||
abs(m1.ay-m2.ay)<=tol and
|
||||
abs(m1.bx-m2.bx)<=tol and
|
||||
abs(m1.by-m2.by)<=tol and
|
||||
abs(m1.tx-m2.tx)<=tol and
|
||||
abs(m1.ty-m2.ty)<=tol
|
||||
|
||||
|
||||
proc `=~`*(m1,m2:Matrix2d):bool=
|
||||
## Checks if `m1`and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
@@ -350,16 +350,16 @@ proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
|
||||
proc len*(v:Vector2d):float {.inline.}=
|
||||
## Returns the length of the vector.
|
||||
sqrt(v.x*v.x+v.y*v.y)
|
||||
|
||||
|
||||
proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its angle.
|
||||
let fac=newlen/v.len
|
||||
|
||||
|
||||
if newlen==0.0:
|
||||
v.x=0.0
|
||||
v.y=0.0
|
||||
return
|
||||
|
||||
|
||||
if fac==Inf or fac==NegInf:
|
||||
#to short for float accuracy
|
||||
#do as good as possible:
|
||||
@@ -368,30 +368,30 @@ proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
|
||||
else:
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
|
||||
proc sqrLen*(v:Vector2d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
v.x*v.x+v.y*v.y
|
||||
|
||||
|
||||
proc angle*(v:Vector2d):float=
|
||||
## Returns the angle of the vector.
|
||||
## Returns the angle of the vector.
|
||||
## (The counter clockwise plane angle between posetive x axis and `v`)
|
||||
result=arctan2(v.y,v.x)
|
||||
if result<0.0: result+=DEG360
|
||||
|
||||
|
||||
proc `$` *(v:Vector2d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
result.add(rtos(v.y))
|
||||
|
||||
|
||||
|
||||
|
||||
proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | 0 0 1 |
|
||||
@@ -403,7 +403,7 @@ proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | 0 0 1 |
|
||||
@@ -412,31 +412,31 @@ proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc tryNormalize*(v:var Vector2d):bool=
|
||||
proc tryNormalize*(v:var Vector2d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and
|
||||
## false is returned, otherwise true is returned.
|
||||
|
||||
let mag=v.len
|
||||
|
||||
if mag==0.0:
|
||||
return false
|
||||
|
||||
|
||||
v.x/=mag
|
||||
v.y/=mag
|
||||
return true
|
||||
|
||||
|
||||
proc normalize*(v:var Vector2d) {.inline.}=
|
||||
proc normalize*(v:var Vector2d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
|
||||
proc transformNorm*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies a normal direction transformation `t` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## (determinant=0), an EDivByZero will be raised.
|
||||
|
||||
# transforming a normal is done by transforming
|
||||
@@ -469,16 +469,16 @@ proc transformInv*(v:var Vector2d,t:Matrix2d)=
|
||||
|
||||
proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies an inverse normal direction transformation `t` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
|
||||
# normal inverse transform is done by transforming
|
||||
# by the inverse of the transpose of the inverse of the org. matrix
|
||||
# which is equivalent with transforming with the transpose.
|
||||
# | | | AX AY 0 |^-1|^T|^-1 | AX BX 0 |
|
||||
# | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 |
|
||||
# | | | 0 0 1 | | | | 0 0 1 |
|
||||
# | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 |
|
||||
# | | | 0 0 1 | | | | 0 0 1 |
|
||||
# This can be heavily reduced to:
|
||||
let newx=t.ay*v.y+t.ax*v.x
|
||||
v.y=t.by*v.y+t.bx*v.x
|
||||
@@ -489,19 +489,19 @@ proc rotate90*(v:var Vector2d) {.inline.}=
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.x= -v.x
|
||||
|
||||
|
||||
proc rotate180*(v:var Vector2d){.inline.}=
|
||||
## Quickly rotates vector `v` 180 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
v.x= -v.x
|
||||
v.y= -v.y
|
||||
|
||||
|
||||
proc rotate270*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 270 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.y= -v.y
|
||||
|
||||
|
||||
proc rotate*(v:var Vector2d,rad:float) =
|
||||
## Rotates vector `v` `rad` radians in place.
|
||||
let
|
||||
@@ -510,18 +510,18 @@ proc rotate*(v:var Vector2d,rad:float) =
|
||||
newx=c*v.x-s*v.y
|
||||
v.y=c*v.y+s*v.x
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc scale*(v:var Vector2d,fac:float){.inline.}=
|
||||
## Scales vector `v` `rad` radians in place.
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
|
||||
proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
|
||||
## Stretches vector `v` `facx` times horizontally,
|
||||
## and `facy` times vertically.
|
||||
v.x*=facx
|
||||
v.y*=facy
|
||||
|
||||
|
||||
proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
|
||||
## Mirrors vector `v` using `mirrvec` as mirror direction.
|
||||
let
|
||||
@@ -530,20 +530,20 @@ proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=mirrvec.x*mirrvec.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return #mirroring around a zero vector is arbitrary=>keep as is is fastest
|
||||
|
||||
|
||||
let newx=xy2*v.y+sqd*v.x
|
||||
v.y=v.x*xy2-sqd*v.y
|
||||
v.x=newx
|
||||
|
||||
|
||||
|
||||
|
||||
proc `-` *(v:Vector2d):Vector2d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
|
||||
|
||||
# declare templated binary operators
|
||||
makeBinOpVector(`+`)
|
||||
makeBinOpVector(`-`)
|
||||
@@ -556,27 +556,27 @@ makeBinOpAssignVector(`/=`)
|
||||
|
||||
|
||||
proc dot*(v1,v2:Vector2d):float=
|
||||
## Computes the dot product of two vectors.
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y
|
||||
|
||||
|
||||
proc cross*(v1,v2:Vector2d):float=
|
||||
## Computes the cross product of two vectors, also called
|
||||
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
|
||||
## are parallel.
|
||||
return v1.x*v2.y-v1.y*v2.x
|
||||
|
||||
|
||||
proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
|
||||
|
||||
|
||||
proc `=~` *(v1,v2:Vector2d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
|
||||
proc angleTo*(v1,v2:Vector2d):float=
|
||||
## Returns the smallest of the two possible angles
|
||||
## Returns the smallest of the two possible angles
|
||||
## between `v1` and `v2` in radians.
|
||||
var
|
||||
nv1=v1
|
||||
@@ -584,7 +584,7 @@ proc angleTo*(v1,v2:Vector2d):float=
|
||||
if not nv1.tryNormalize or not nv2.tryNormalize:
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
|
||||
proc angleCCW*(v1,v2:Vector2d):float=
|
||||
## Returns the counter clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
@@ -592,7 +592,7 @@ proc angleCCW*(v1,v2:Vector2d):float=
|
||||
if v1.cross(v2)>=0.0:
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
|
||||
proc angleCW*(v1,v2:Vector2d):float=
|
||||
## Returns the clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
@@ -612,32 +612,32 @@ proc turnAngle*(v1,v2:Vector2d):float=
|
||||
proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
## an arbitrary normalized vector is returned.
|
||||
var
|
||||
vmag1=v1.len
|
||||
vmag2=v2.len
|
||||
|
||||
|
||||
# zero length vector equals arbitrary vector, just change to magnitude to one to
|
||||
# avoid zero division
|
||||
if vmag1==0.0:
|
||||
if vmag1==0.0:
|
||||
if vmag2==0: #both are zero length return any normalized vector
|
||||
return XAXIS
|
||||
vmag1=1.0
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
let
|
||||
x1=v1.x/vmag1
|
||||
y1=v1.y/vmag1
|
||||
x2=v2.x/vmag2
|
||||
y2=v2.y/vmag2
|
||||
|
||||
|
||||
result.x=(x1 + x2) * 0.5
|
||||
result.y=(y1 + y2) * 0.5
|
||||
|
||||
|
||||
if not result.tryNormalize():
|
||||
# This can happen if vectors are colinear. In this special case
|
||||
# there are actually two bisectors, we select just
|
||||
# there are actually two bisectors, we select just
|
||||
# one of them (x1,y1 rotated 90 degrees ccw).
|
||||
result.x = -y1
|
||||
result.y = x1
|
||||
@@ -651,13 +651,13 @@ proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
|
||||
proc point2d*(x,y:float):Point2d =
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
|
||||
proc sqrDist*(a,b:Point2d):float=
|
||||
## Computes the squared distance between `a` and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=dx*dx+dy*dy
|
||||
|
||||
|
||||
proc dist*(a,b:Point2d):float {.inline.}=
|
||||
## Computes the absolute distance between `a` and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
@@ -675,11 +675,11 @@ proc `$` *(p:Point2d):string=
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
result.add(rtos(p.y))
|
||||
|
||||
|
||||
proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
|
||||
## Concatenates a point `p` with a transform `t`,
|
||||
## resulting in a new, transformed point.
|
||||
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | TX TY 1 |
|
||||
@@ -697,21 +697,21 @@ proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
|
||||
## Applies the inverse of transformation `t` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
|
||||
|
||||
# | AX AY 0 | ^-1
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | TX TY 1 |
|
||||
let d=t.determinant
|
||||
if d==0.0:
|
||||
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
|
||||
let
|
||||
let
|
||||
newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d
|
||||
p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d
|
||||
p.x=newx
|
||||
|
||||
|
||||
|
||||
|
||||
proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
@@ -722,7 +722,7 @@ proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
p.y+=v.y
|
||||
|
||||
proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
@@ -736,13 +736,13 @@ proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
|
||||
|
||||
proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:Point2d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
@@ -759,7 +759,7 @@ proc rotate*(p:var Point2d,rad:float)=
|
||||
newx=p.x*c-p.y*s
|
||||
p.y=p.y*c+p.x*s
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc rotate*(p:var Point2d,rad:float,org:Point2d)=
|
||||
## Rotates a point in place `rad` radians using `org` as
|
||||
## center of rotation.
|
||||
@@ -769,25 +769,25 @@ proc rotate*(p:var Point2d,rad:float,org:Point2d)=
|
||||
newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x
|
||||
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc scale*(p:var Point2d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
|
||||
|
||||
proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
|
||||
proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` and `facy` times with
|
||||
## Scales a point in place non uniformly `facx` and `facy` times with
|
||||
## world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
|
||||
proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` and `facy` times with
|
||||
## Scales the point in place non uniformly `facx` and `facy` times with
|
||||
## `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
p.y=(p.y - org.y) * facy + org.y
|
||||
@@ -814,21 +814,21 @@ proc area*(a,b,c:Point2d):float=
|
||||
return abs(sgnArea(a,b,c))
|
||||
|
||||
proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
|
||||
## Returns a point selected from `pts`, that has the closest
|
||||
## Returns a point selected from `pts`, that has the closest
|
||||
## euclidean distance to `p`
|
||||
assert(pts.len>0) # must have at least one point
|
||||
|
||||
var
|
||||
|
||||
var
|
||||
bestidx=0
|
||||
bestdist=p.sqrDist(pts[0])
|
||||
curdist:float
|
||||
|
||||
|
||||
for idx in 1..high(pts):
|
||||
curdist=p.sqrDist(pts[idx])
|
||||
if curdist<bestdist:
|
||||
bestidx=idx
|
||||
bestdist=curdist
|
||||
|
||||
|
||||
result=pts[bestidx]
|
||||
|
||||
|
||||
@@ -843,7 +843,7 @@ proc normAngle*(ang:float):float=
|
||||
return ang
|
||||
|
||||
return ang mod DEG360
|
||||
|
||||
|
||||
proc degToRad*(deg:float):float {.inline.}=
|
||||
## converts `deg` degrees to radians
|
||||
deg / RAD2DEGCONST
|
||||
@@ -852,4 +852,4 @@ proc radToDeg*(rad:float):float {.inline.}=
|
||||
## converts `rad` radians to degrees
|
||||
rad * RAD2DEGCONST
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,32 +6,32 @@ import unsigned, math, hashes
|
||||
## Memory Utility Functions
|
||||
|
||||
proc newHeap*[T](): ptr T =
|
||||
result = cast[ptr T](alloc0(sizeof(T)))
|
||||
result = cast[ptr T](alloc0(sizeof(T)))
|
||||
|
||||
proc copyNew*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = alloc(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = alloc(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
proc copyTo*[T](val: var T, dest: int) =
|
||||
copyMem(pointer(dest), val.addr, sizeof(T))
|
||||
proc copyTo*[T](val: var T, dest: int) =
|
||||
copyMem(pointer(dest), val.addr, sizeof(T))
|
||||
|
||||
proc allocType*[T](): pointer = alloc(sizeof(T))
|
||||
proc allocType*[T](): pointer = alloc(sizeof(T))
|
||||
|
||||
proc newShared*[T](): ptr T =
|
||||
result = cast[ptr T](allocShared0(sizeof(T)))
|
||||
result = cast[ptr T](allocShared0(sizeof(T)))
|
||||
|
||||
proc copyShared*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = allocShared(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = allocShared(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Pointer arithmetic
|
||||
## Pointer arithmetic
|
||||
|
||||
proc `+`*(p: pointer, i: int): pointer {.inline.} =
|
||||
cast[pointer](cast[int](p) + i)
|
||||
@@ -41,11 +41,11 @@ const
|
||||
reProbeLimit = 12
|
||||
minCopyWork = 4096
|
||||
intSize = sizeof(int)
|
||||
|
||||
|
||||
|
||||
|
||||
when sizeof(int) == 4: # 32bit
|
||||
type
|
||||
type
|
||||
Raw = range[0..1073741823]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 32 bit platform
|
||||
@@ -56,40 +56,40 @@ elif sizeof(int) == 8: # 64bit
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 64 bit platform
|
||||
{.deprecated: [TRaw: Raw].}
|
||||
else:
|
||||
else:
|
||||
{.error: "unsupported platform".}
|
||||
|
||||
type
|
||||
|
||||
type
|
||||
Entry = tuple
|
||||
key: int
|
||||
value: int
|
||||
|
||||
EntryArr = ptr array[0..10_000_000, Entry]
|
||||
|
||||
|
||||
PConcTable[K,V] = ptr object {.pure.}
|
||||
len: int
|
||||
used: int
|
||||
active: int
|
||||
copyIdx: int
|
||||
copyDone: int
|
||||
next: PConcTable[K,V]
|
||||
next: PConcTable[K,V]
|
||||
data: EntryArr
|
||||
{.deprecated: [TEntry: Entry, TEntryArr: EntryArr.}
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int
|
||||
expVal: int, match: bool): int
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Create a new table
|
||||
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
let
|
||||
dataLen = max(nextPowerOfTwo(size), minTableSize)
|
||||
dataSize = dataLen*sizeof(Entry)
|
||||
dataMem = allocShared0(dataSize)
|
||||
let
|
||||
dataLen = max(nextPowerOfTwo(size), minTableSize)
|
||||
dataSize = dataLen*sizeof(Entry)
|
||||
dataMem = allocShared0(dataSize)
|
||||
tableSize = 7 * intSize
|
||||
tableMem = allocShared0(tableSize)
|
||||
table = cast[PConcTable[K,V]](tableMem)
|
||||
table = cast[PConcTable[K,V]](tableMem)
|
||||
table.len = dataLen
|
||||
table.used = 0
|
||||
table.active = 0
|
||||
@@ -99,14 +99,14 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
table.data = cast[EntryArr](dataMem)
|
||||
result = table
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Delete a table
|
||||
proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
|
||||
deallocShared(tbl.data)
|
||||
deallocShared(tbl.data)
|
||||
deallocShared(tbl)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
|
||||
table.data[i]
|
||||
@@ -119,15 +119,15 @@ proc pack[T](x: T): int {.inline.} =
|
||||
result = (cast[int](x) shl 2)
|
||||
#echo("packKey ",cast[int](x) , " -> ", result)
|
||||
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
proc pop(x: int): int {.inline.} =
|
||||
result = x and 0xFFFFFFFC'i32
|
||||
|
||||
# Pop the raw value off of our Key or Val
|
||||
# Pop the raw value off of our Key or Val
|
||||
proc popRaw(x: int): int {.inline.} =
|
||||
result = x shr 2
|
||||
result = x shr 2
|
||||
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
proc popPtr[V](x: int): ptr V {.inline.} =
|
||||
result = cast[ptr V](pop(x))
|
||||
#echo("popPtr " & $x & " -> " & $cast[int](result))
|
||||
@@ -136,34 +136,34 @@ proc popPtr[V](x: int): ptr V {.inline.} =
|
||||
# K or V is no longer valid use new table
|
||||
const Ghost = 0xFFFFFFFC
|
||||
proc isGhost(x: int): bool {.inline.} =
|
||||
result = x == 0xFFFFFFFC
|
||||
result = x == 0xFFFFFFFC
|
||||
|
||||
# Tombstone
|
||||
# applied to V = K is dead
|
||||
proc isTomb(x: int): bool {.inline.} =
|
||||
# Tombstone
|
||||
# applied to V = K is dead
|
||||
proc isTomb(x: int): bool {.inline.} =
|
||||
result = (x and 0x00000002) != 0
|
||||
|
||||
proc setTomb(x: int): int {.inline.} =
|
||||
result = x or 0x00000002
|
||||
|
||||
# Prime
|
||||
# K or V is in new table copied from old
|
||||
proc isPrime(x: int): bool {.inline.} =
|
||||
# K or V is in new table copied from old
|
||||
proc isPrime(x: int): bool {.inline.} =
|
||||
result = (x and 0x00000001) != 0
|
||||
|
||||
proc setPrime(x: int): int {.inline.} =
|
||||
result = x or 0x00000001
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
##This is for i32 only need to override for i64
|
||||
proc hashInt(x: int):int {.inline.} =
|
||||
var h = uint32(x) #shr 2'u32
|
||||
proc hashInt(x: int):int {.inline.} =
|
||||
var h = uint32(x) #shr 2'u32
|
||||
h = h xor (h shr 16'u32)
|
||||
h *= 0x85ebca6b'u32
|
||||
h = h xor (h shr 13'u32)
|
||||
h *= 0xc2b2ae35'u32
|
||||
h = h xor (h shr 16'u32)
|
||||
h = h xor (h shr 16'u32)
|
||||
result = int(h)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -175,31 +175,31 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
|
||||
#echo("A new table already exists, copy in progress")
|
||||
return next
|
||||
var
|
||||
oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
|
||||
oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
|
||||
newTable = newLFTable[K,V](oldLen*2)
|
||||
success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable,
|
||||
false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if not success:
|
||||
echo("someone beat us to it! delete table we just created and return his " & $cast[int](next))
|
||||
deleteConcTable(newTable)
|
||||
return next
|
||||
deleteConcTable(newTable)
|
||||
return next
|
||||
else:
|
||||
echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len)
|
||||
return newTable
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
|
||||
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
|
||||
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
result = false
|
||||
when K is Raw:
|
||||
if key1 == key2:
|
||||
if key1 == key2:
|
||||
result = true
|
||||
else:
|
||||
var
|
||||
var
|
||||
p1 = popPtr[K](key1)
|
||||
p2 = popPtr[K](key2)
|
||||
if p1 != nil and p2 != nil:
|
||||
if p1 != nil and p2 != nil:
|
||||
if cast[int](p1) == cast[int](p2):
|
||||
return true
|
||||
if p1[] == p2[]:
|
||||
@@ -214,53 +214,53 @@ proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
|
||||
proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable[K,V]): bool =
|
||||
#echo("Copy idx " & $idx)
|
||||
var
|
||||
var
|
||||
oldVal = 0
|
||||
oldkey = 0
|
||||
oldkey = 0
|
||||
ok = false
|
||||
result = false
|
||||
#Block the key so no other threads waste time here
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
|
||||
setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey))
|
||||
#Prevent new values from appearing in the old table by priming
|
||||
#echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey))
|
||||
#Prevent new values from appearing in the old table by priming
|
||||
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
|
||||
while not isPrime(oldVal):
|
||||
var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||
else: oldVal.setPrime
|
||||
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||
else: oldVal.setPrime
|
||||
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
|
||||
if isPrime(box) and isTomb(box):
|
||||
if isPrime(box) and isTomb(box):
|
||||
return true
|
||||
oldVal = box
|
||||
break
|
||||
#echo("oldVal was = ", oldVal, " set it to prime ", box)
|
||||
if isPrime(oldVal) and isTomb(oldVal):
|
||||
if isPrime(oldVal) and isTomb(oldVal):
|
||||
#when not (K is Raw):
|
||||
# deallocShared(popPtr[K](oldKey))
|
||||
# deallocShared(popPtr[K](oldKey))
|
||||
return false
|
||||
if isTomb(oldVal):
|
||||
if isTomb(oldVal):
|
||||
echo("oldVal is Tomb!!!, should not happen")
|
||||
if pop(oldVal) != 0:
|
||||
if pop(oldVal) != 0:
|
||||
result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
|
||||
if result:
|
||||
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
||||
else:
|
||||
#echo("copy slot failed")
|
||||
if result:
|
||||
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
||||
else:
|
||||
#echo("copy slot failed")
|
||||
# Our copy is done so we disable the old slot
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
oldVal.setTomb.setPrime , false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("disabled old slot")
|
||||
#echo"---------------------"
|
||||
#echo("disabled old slot")
|
||||
#echo"---------------------"
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc promote[K,V](table: var PConcTable[K,V]) =
|
||||
var
|
||||
newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED)
|
||||
newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
|
||||
newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
|
||||
newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED)
|
||||
|
||||
deallocShared(table.data)
|
||||
@@ -270,52 +270,52 @@ proc promote[K,V](table: var PConcTable[K,V]) =
|
||||
atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED)
|
||||
deallocShared(table.next)
|
||||
atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
|
||||
echo("new table swapped!")
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
|
||||
var
|
||||
|
||||
proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
|
||||
ok: bool
|
||||
result = false
|
||||
result = false
|
||||
if workDone > 0:
|
||||
#echo("len to copy =" & $oldLen)
|
||||
#echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
|
||||
#echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
|
||||
ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
|
||||
copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#if ok: echo("set copyDone")
|
||||
# If the copy is done we can promote this table
|
||||
#if ok: echo("set copyDone")
|
||||
# If the copy is done we can promote this table
|
||||
if copyDone + workDone >= oldLen:
|
||||
# Swap new data
|
||||
#echo("work is done!")
|
||||
#echo("work is done!")
|
||||
table.promote
|
||||
result = true
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc copySlotAndCheck[K,V](table: var PConcTable[K,V], idx: int):
|
||||
PConcTable[K,V] =
|
||||
var
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil and copySlot(idx, table, newTable):
|
||||
#echo("copied a single slot, idx = " & $idx)
|
||||
result = newTable
|
||||
if newTable != nil and copySlot(idx, table, newTable):
|
||||
#echo("copied a single slot, idx = " & $idx)
|
||||
if checkAndPromote(table, 1): return table
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
|
||||
var
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil:
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil:
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
|
||||
copyIdx = 0
|
||||
work = min(oldLen, minCopyWork)
|
||||
@@ -324,54 +324,54 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
|
||||
if copyDone < oldLen:
|
||||
var ok: bool
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
|
||||
ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
|
||||
copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("copy idx = ", copyIdx)
|
||||
#echo("copy idx = ", copyIdx)
|
||||
for i in 0..work-1:
|
||||
var idx = (copyIdx + i) and (oldLen - 1)
|
||||
var idx = (copyIdx + i) and (oldLen - 1)
|
||||
if copySlot(idx, table, newTable):
|
||||
workDone += 1
|
||||
if workDone > 0:
|
||||
#echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]()))
|
||||
if checkAndPromote(table, workDone): return table
|
||||
# In case a thread finished all the work then got stalled before promotion
|
||||
# In case a thread finished all the work then got stalled before promotion
|
||||
if checkAndPromote(table, 0): return table
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int =
|
||||
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
var
|
||||
nextTable: PConcTable[K,V]
|
||||
var idx = popPtr[K](key)[].hash
|
||||
var
|
||||
nextTable: PConcTable[K,V]
|
||||
probes = 1
|
||||
# spin until we find a key slot or build and jump to next table
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
# spin until we find a key slot or build and jump to next table
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
#echo("try set idx = " & $idx & "for" & $key)
|
||||
var
|
||||
probedKey = 0
|
||||
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
||||
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
probedKey = 0
|
||||
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
||||
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if openKey:
|
||||
if val.isTomb:
|
||||
#echo("val was tomb, bail, no reason to set an open slot to tomb")
|
||||
return val
|
||||
#increment used slots
|
||||
#echo("found an open slot, total used = " &
|
||||
#increment used slots
|
||||
#echo("found an open slot, total used = " &
|
||||
#$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED))
|
||||
discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)
|
||||
break # We found an open slot
|
||||
#echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
|
||||
break # We found an open slot
|
||||
#echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("we found the matching slot")
|
||||
break # We found a matching slot
|
||||
#echo("we found the matching slot")
|
||||
break # We found a matching slot
|
||||
if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb):
|
||||
if key.isTomb: echo("Key is Tombstone")
|
||||
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
|
||||
@@ -379,22 +379,22 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
#create next bigger table
|
||||
nextTable = resize(table)
|
||||
#help do some copying
|
||||
#echo("help copy old table to new")
|
||||
nextTable = helpCopy(table)
|
||||
#echo("help copy old table to new")
|
||||
nextTable = helpCopy(table)
|
||||
#now setVal in the new table instead
|
||||
#echo("jumping to next table to set val")
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
#echo("jumping to next table to set val")
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
else:
|
||||
idx += 1
|
||||
probes += 1
|
||||
# Done spinning for a new slot
|
||||
var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
|
||||
var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
|
||||
if val == oldVal:
|
||||
#echo("this val is alredy in the slot")
|
||||
#echo("this val is alredy in the slot")
|
||||
return oldVal
|
||||
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
||||
if nextTable == nil and
|
||||
((oldVal == 0 and
|
||||
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
||||
if nextTable == nil and
|
||||
((oldVal == 0 and
|
||||
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
|
||||
(isPrime(oldVal))):
|
||||
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
|
||||
@@ -402,7 +402,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event")
|
||||
nextTable = resize(table)
|
||||
if nextTable != nil:
|
||||
#echo("tomb old slot then set in new table")
|
||||
#echo("tomb old slot then set in new table")
|
||||
nextTable = copySlotAndCheck(table,idx)
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
# Finally ready to add new val to table
|
||||
@@ -410,7 +410,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
if match and oldVal != expVal:
|
||||
#echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal)
|
||||
return oldVal
|
||||
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
||||
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
||||
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
||||
#echo("val set at table " & $cast[int](table))
|
||||
if expVal != 0:
|
||||
@@ -427,48 +427,48 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
#echo("-try get- key = " & $key)
|
||||
when K is Raw:
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
#echo("get idx ", idx)
|
||||
var
|
||||
var idx = popPtr[K](key)[].hash
|
||||
#echo("get idx ", idx)
|
||||
var
|
||||
probes = 0
|
||||
val: int
|
||||
val: int
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
var
|
||||
idx = idx and (table.len - 1)
|
||||
var
|
||||
newTable: PConcTable[K,V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE)
|
||||
probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
|
||||
probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("found key after ", probes+1)
|
||||
val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE)
|
||||
if not isPrime(val):
|
||||
if isTomb(val):
|
||||
#echo("val was tomb but not prime")
|
||||
#echo("val was tomb but not prime")
|
||||
return 0
|
||||
else:
|
||||
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
||||
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
||||
return val
|
||||
else:
|
||||
newTable = copySlotAndCheck(table, idx)
|
||||
return getVal(newTable, key)
|
||||
return getVal(newTable, key)
|
||||
else:
|
||||
#echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
|
||||
#echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
|
||||
if probes >= reProbeLimit*4 or key.isTomb:
|
||||
if newTable == nil:
|
||||
#echo("too many probes and no new table ", key, " ", idx )
|
||||
return 0
|
||||
else:
|
||||
else:
|
||||
newTable = helpCopy(table)
|
||||
return getVal(newTable, key)
|
||||
idx += 1
|
||||
probes += 1
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
|
||||
# discard setVal(table, pack(key), pack(key), 0, false)
|
||||
|
||||
@@ -476,33 +476,34 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
# discard setVal(table, pack(key), cast[int](val), 0, false)
|
||||
|
||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
when not (K is Raw):
|
||||
when not (K is Raw):
|
||||
var newKey = cast[int](copyShared(key))
|
||||
else:
|
||||
else:
|
||||
var newKey = pack(key)
|
||||
when not (V is Raw):
|
||||
when not (V is Raw):
|
||||
var newVal = cast[int](copyShared(val))
|
||||
else:
|
||||
else:
|
||||
var newVal = pack(val)
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
|
||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||
when not (V is Raw):
|
||||
if newVal != oldPtr and oldPtr != 0:
|
||||
when not (V is Raw):
|
||||
if newVal != oldPtr and oldPtr != 0:
|
||||
deallocShared(cast[ptr V](oldPtr))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
when not (V is Raw):
|
||||
when not (K is Raw):
|
||||
return popPtr[V](getVal(table, cast[int](key.addr)))[]
|
||||
else:
|
||||
else:
|
||||
return popPtr[V](getVal(table, pack(key)))[]
|
||||
else:
|
||||
when not (K is Raw):
|
||||
return popRaw(getVal(table, cast[int](key.addr)))
|
||||
else:
|
||||
return popRaw(getVal(table, pack(key)))
|
||||
else:
|
||||
return popRaw(getVal(table, pack(key)))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -512,7 +513,6 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} =
|
||||
# getVal(table, key)
|
||||
@@ -528,16 +528,16 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
#Tests ----------------------------
|
||||
when not defined(testing) and isMainModule:
|
||||
import locks, times, mersenne
|
||||
|
||||
const
|
||||
|
||||
const
|
||||
numTests = 100000
|
||||
numThreads = 10
|
||||
|
||||
|
||||
|
||||
|
||||
type
|
||||
TestObj = tuple
|
||||
thr: int
|
||||
thr: int
|
||||
f0: int
|
||||
f1: int
|
||||
|
||||
@@ -545,63 +545,63 @@ when not defined(testing) and isMainModule:
|
||||
PDataArr = array[0..numTests-1, Data]
|
||||
Dict = PConcTable[string,TestObj]
|
||||
{.deprecated: [TTestObj: TestObj, TData: Data].}
|
||||
|
||||
var
|
||||
|
||||
var
|
||||
thr: array[0..numThreads-1, Thread[Dict]]
|
||||
|
||||
table = newLFTable[string,TestObj](8)
|
||||
|
||||
table = newLFTable[string,TestObj](8)
|
||||
rand = newMersenneTwister(2525)
|
||||
|
||||
proc createSampleData(len: int): PDataArr =
|
||||
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
|
||||
proc createSampleData(len: int): PDataArr =
|
||||
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
|
||||
for i in 0..len-1:
|
||||
result[i].k = "mark" & $(i+1)
|
||||
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
|
||||
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
|
||||
result[i].v.thr = 0
|
||||
result[i].v.f0 = i+1
|
||||
result[i].v.f1 = 0
|
||||
result[i].v.f0 = i+1
|
||||
result[i].v.f1 = 0
|
||||
#echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr))
|
||||
|
||||
|
||||
|
||||
proc threadProc(tp: Dict) {.thread.} =
|
||||
var t = cpuTime();
|
||||
proc threadProc(tp: Dict) {.thread.} =
|
||||
var t = cpuTime();
|
||||
for i in 1..numTests:
|
||||
var key = "mark" & $(i)
|
||||
var got = table.get(key)
|
||||
var got = table.get(key)
|
||||
got.thr = cast[int](myThreadID[pointer]())
|
||||
got.f1 = got.f1 + 1
|
||||
got.f1 = got.f1 + 1
|
||||
table.set(key, got)
|
||||
t = cpuTime() - t
|
||||
echo t
|
||||
|
||||
echo t
|
||||
|
||||
|
||||
var testData = createSampleData(numTests)
|
||||
|
||||
for i in 0..numTests-1:
|
||||
table.set(testData[i].k, testData[i].v)
|
||||
|
||||
|
||||
var i = 0
|
||||
while i < numThreads:
|
||||
createThread(thr[i], threadProc, table)
|
||||
i += 1
|
||||
|
||||
joinThreads(thr)
|
||||
joinThreads(thr)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var fails = 0
|
||||
|
||||
for i in 0..numTests-1:
|
||||
var got = table.get(testData[i].k)
|
||||
for i in 0..numTests-1:
|
||||
var got = table.get(testData[i].k)
|
||||
if got.f0 != i+1 or got.f1 != numThreads:
|
||||
fails += 1
|
||||
echo(got)
|
||||
|
||||
echo("Failed read or write = ", fails)
|
||||
|
||||
|
||||
|
||||
#for i in 1..numTests:
|
||||
# echo(i, " = ", hashInt(i) and 8191)
|
||||
|
||||
@@ -17,11 +17,11 @@ type
|
||||
otherbits: char
|
||||
case isLeaf: bool
|
||||
of false: child: array[0..1, ref NodeObj[T]]
|
||||
of true:
|
||||
of true:
|
||||
key: string
|
||||
when T isnot void:
|
||||
val: T
|
||||
|
||||
|
||||
Node[T] = ref NodeObj[T]
|
||||
CritBitTree*[T] = object ## The crit bit tree can either be used
|
||||
## as a mapping from strings to
|
||||
@@ -66,7 +66,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
|
||||
let ch = if it.byte < key.len: key[it.byte] else: '\0'
|
||||
let dir = (1 + (ch.ord or it.otherBits.ord)) shr 8
|
||||
it = it.child[dir]
|
||||
|
||||
|
||||
var newOtherBits = 0
|
||||
var newByte = 0
|
||||
block blockX:
|
||||
@@ -84,7 +84,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
|
||||
newOtherBits = newOtherBits xor 255
|
||||
let ch = it.key[newByte]
|
||||
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
|
||||
|
||||
|
||||
var inner: Node[T]
|
||||
new inner
|
||||
new result
|
||||
@@ -93,7 +93,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
|
||||
inner.otherBits = chr(newOtherBits)
|
||||
inner.byte = newByte
|
||||
inner.child[1 - dir] = result
|
||||
|
||||
|
||||
var wherep = addr(c.root)
|
||||
while true:
|
||||
var p = wherep[]
|
||||
@@ -176,7 +176,7 @@ iterator leaves[T](n: Node[T]): Node[T] =
|
||||
# XXX actually we could compute the necessary stack size in advance:
|
||||
# it's roughly log2(c.count).
|
||||
var stack = @[n]
|
||||
while stack.len > 0:
|
||||
while stack.len > 0:
|
||||
var it = stack.pop
|
||||
while not it.isLeaf:
|
||||
stack.add(it.child[1])
|
||||
@@ -205,7 +205,7 @@ iterator items*[T](c: CritBitTree[T]): string =
|
||||
iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
|
||||
## yields all (key, value)-pairs of `c`.
|
||||
for x in leaves(c.root): yield (x.key, x.val)
|
||||
|
||||
|
||||
iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
|
||||
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
||||
for x in leaves(c.root): yield (x.key, x.val)
|
||||
@@ -251,7 +251,7 @@ iterator pairsWithPrefix*[T](c: CritBitTree[T],
|
||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield (x.key, x.val)
|
||||
|
||||
|
||||
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
|
||||
prefix: string): tuple[key: string, val: var T] =
|
||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||
|
||||
@@ -27,18 +27,18 @@ type
|
||||
|
||||
SinglyLinkedList*[T] = object ## a singly linked list
|
||||
head*, tail*: SinglyLinkedNode[T]
|
||||
|
||||
|
||||
DoublyLinkedList*[T] = object ## a doubly linked list
|
||||
head*, tail*: DoublyLinkedNode[T]
|
||||
|
||||
SinglyLinkedRing*[T] = object ## a singly linked ring
|
||||
head*, tail*: SinglyLinkedNode[T]
|
||||
|
||||
|
||||
DoublyLinkedRing*[T] = object ## a doubly linked ring
|
||||
head*: DoublyLinkedNode[T]
|
||||
|
||||
{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
|
||||
PDoublyLinkedNode: DoublyLinkedNode,
|
||||
PDoublyLinkedNode: DoublyLinkedNode,
|
||||
TSinglyLinkedNode: SinglyLinkedNodeObj,
|
||||
PSinglyLinkedNode: SinglyLinkedNode,
|
||||
TDoublyLinkedList: DoublyLinkedList,
|
||||
@@ -106,19 +106,19 @@ template findImpl() {.dirty.} =
|
||||
for x in nodes(L):
|
||||
if x.value == value: return x
|
||||
|
||||
iterator items*[T](L: DoublyLinkedList[T]): T =
|
||||
iterator items*[T](L: DoublyLinkedList[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsListImpl()
|
||||
|
||||
iterator items*[T](L: SinglyLinkedList[T]): T =
|
||||
iterator items*[T](L: SinglyLinkedList[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsListImpl()
|
||||
|
||||
iterator items*[T](L: SinglyLinkedRing[T]): T =
|
||||
iterator items*[T](L: SinglyLinkedRing[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsRingImpl()
|
||||
|
||||
iterator items*[T](L: DoublyLinkedRing[T]): T =
|
||||
iterator items*[T](L: DoublyLinkedRing[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsRingImpl()
|
||||
|
||||
@@ -138,22 +138,22 @@ iterator mitems*[T](L: var DoublyLinkedRing[T]): var T =
|
||||
## yields every value of `L` so that you can modify it.
|
||||
itemsRingImpl()
|
||||
|
||||
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
|
||||
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesListImpl()
|
||||
|
||||
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
|
||||
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesListImpl()
|
||||
|
||||
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
|
||||
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesRingImpl()
|
||||
|
||||
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
|
||||
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesRingImpl()
|
||||
@@ -165,87 +165,87 @@ template dollarImpl() {.dirty.} =
|
||||
result.add($x.value)
|
||||
result.add("]")
|
||||
|
||||
proc `$`*[T](L: SinglyLinkedList[T]): string =
|
||||
proc `$`*[T](L: SinglyLinkedList[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc `$`*[T](L: DoublyLinkedList[T]): string =
|
||||
proc `$`*[T](L: DoublyLinkedList[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc `$`*[T](L: SinglyLinkedRing[T]): string =
|
||||
proc `$`*[T](L: SinglyLinkedRing[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc `$`*[T](L: DoublyLinkedRing[T]): string =
|
||||
proc `$`*[T](L: DoublyLinkedRing[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
|
||||
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
|
||||
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
|
||||
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
|
||||
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
|
||||
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
|
||||
proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
|
||||
proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
|
||||
proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
|
||||
proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
|
||||
## searches in the list for a value. Returns false if the value does not
|
||||
## exist, true otherwise.
|
||||
result = find(L, value) != nil
|
||||
|
||||
proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
|
||||
proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
|
||||
## searches in the list for a value. Returns false if the value does not
|
||||
## exist, true otherwise.
|
||||
result = find(L, value) != nil
|
||||
|
||||
proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
|
||||
proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
|
||||
## searches in the list for a value. Returns false if the value does not
|
||||
## exist, true otherwise.
|
||||
result = find(L, value) != nil
|
||||
|
||||
proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
|
||||
proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
|
||||
## searches in the list for a value. Returns false if the value does not
|
||||
## exist, true otherwise.
|
||||
result = find(L, value) != nil
|
||||
|
||||
proc prepend*[T](L: var SinglyLinkedList[T],
|
||||
n: SinglyLinkedNode[T]) {.inline.} =
|
||||
proc prepend*[T](L: var SinglyLinkedList[T],
|
||||
n: SinglyLinkedNode[T]) {.inline.} =
|
||||
## prepends a node to `L`. Efficiency: O(1).
|
||||
n.next = L.head
|
||||
L.head = n
|
||||
|
||||
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
|
||||
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
|
||||
## prepends a node to `L`. Efficiency: O(1).
|
||||
prepend(L, newSinglyLinkedNode(value))
|
||||
|
||||
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
|
||||
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
## appends a node `n` to `L`. Efficiency: O(1).
|
||||
n.next = nil
|
||||
n.prev = L.tail
|
||||
if L.tail != nil:
|
||||
if L.tail != nil:
|
||||
assert(L.tail.next == nil)
|
||||
L.tail.next = n
|
||||
L.tail = n
|
||||
if L.head == nil: L.head = n
|
||||
|
||||
proc append*[T](L: var DoublyLinkedList[T], value: T) =
|
||||
proc append*[T](L: var DoublyLinkedList[T], value: T) =
|
||||
## appends a value to `L`. Efficiency: O(1).
|
||||
append(L, newDoublyLinkedNode(value))
|
||||
|
||||
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
## prepends a node `n` to `L`. Efficiency: O(1).
|
||||
n.prev = nil
|
||||
n.next = L.head
|
||||
@@ -255,11 +255,11 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
L.head = n
|
||||
if L.tail == nil: L.tail = n
|
||||
|
||||
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
|
||||
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
|
||||
## prepends a value to `L`. Efficiency: O(1).
|
||||
prepend(L, newDoublyLinkedNode(value))
|
||||
|
||||
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
|
||||
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
## removes `n` from `L`. Efficiency: O(1).
|
||||
if n == L.tail: L.tail = n.prev
|
||||
if n == L.head: L.head = n.next
|
||||
@@ -267,7 +267,7 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||
if n.prev != nil: n.prev.next = n.next
|
||||
|
||||
|
||||
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
## appends a node `n` to `L`. Efficiency: O(1).
|
||||
if L.head != nil:
|
||||
n.next = L.head
|
||||
@@ -279,11 +279,11 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
L.head = n
|
||||
L.tail = n
|
||||
|
||||
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
|
||||
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
|
||||
## appends a value to `L`. Efficiency: O(1).
|
||||
append(L, newSinglyLinkedNode(value))
|
||||
|
||||
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
## prepends a node `n` to `L`. Efficiency: O(1).
|
||||
if L.head != nil:
|
||||
n.next = L.head
|
||||
@@ -294,11 +294,11 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
L.tail = n
|
||||
L.head = n
|
||||
|
||||
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
|
||||
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
|
||||
## prepends a value to `L`. Efficiency: O(1).
|
||||
prepend(L, newSinglyLinkedNode(value))
|
||||
|
||||
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
## appends a node `n` to `L`. Efficiency: O(1).
|
||||
if L.head != nil:
|
||||
n.next = L.head
|
||||
@@ -310,13 +310,13 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
n.next = n
|
||||
L.head = n
|
||||
|
||||
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
|
||||
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
|
||||
## appends a value to `L`. Efficiency: O(1).
|
||||
append(L, newDoublyLinkedNode(value))
|
||||
|
||||
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
## prepends a node `n` to `L`. Efficiency: O(1).
|
||||
if L.head != nil:
|
||||
if L.head != nil:
|
||||
n.next = L.head
|
||||
n.prev = L.head.prev
|
||||
L.head.prev.next = n
|
||||
@@ -326,17 +326,17 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
n.next = n
|
||||
L.head = n
|
||||
|
||||
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
|
||||
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
|
||||
## prepends a value to `L`. Efficiency: O(1).
|
||||
prepend(L, newDoublyLinkedNode(value))
|
||||
|
||||
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
|
||||
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
## removes `n` from `L`. Efficiency: O(1).
|
||||
n.next.prev = n.prev
|
||||
n.prev.next = n.next
|
||||
if n == L.head:
|
||||
if n == L.head:
|
||||
var p = L.head.prev
|
||||
if p == L.head:
|
||||
if p == L.head:
|
||||
# only one element left:
|
||||
L.head = nil
|
||||
else:
|
||||
|
||||
@@ -77,7 +77,7 @@ proc dequeue*[T](q: var Queue[T]): T =
|
||||
result = q.data[q.rd]
|
||||
q.rd = (q.rd + 1) and q.mask
|
||||
|
||||
proc `$`*[T](q: Queue[T]): string =
|
||||
proc `$`*[T](q: Queue[T]): string =
|
||||
## turns a queue into its string representation.
|
||||
result = "["
|
||||
for x in items(q):
|
||||
@@ -95,7 +95,7 @@ when isMainModule:
|
||||
q.add(6)
|
||||
var second = q.dequeue
|
||||
q.add(789)
|
||||
|
||||
|
||||
assert first == 123
|
||||
assert second == 9
|
||||
assert($q == "[4, 56, 6, 789]")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements color handling for Nimrod. It is used by
|
||||
## This module implements color handling for Nimrod. It is used by
|
||||
## the ``graphics`` module.
|
||||
|
||||
import strutils
|
||||
@@ -18,15 +18,15 @@ type
|
||||
|
||||
proc `==` *(a, b: Color): bool {.borrow.}
|
||||
## compares two colors.
|
||||
|
||||
|
||||
template extract(a: Color, r, g, b: expr) {.immediate.}=
|
||||
var r = a.int shr 16 and 0xff
|
||||
var g = a.int shr 8 and 0xff
|
||||
var b = a.int and 0xff
|
||||
|
||||
|
||||
template rawRGB(r, g, b: int): expr =
|
||||
Color(r shl 16 or g shl 8 or b)
|
||||
|
||||
|
||||
template colorOp(op: expr) {.immediate.} =
|
||||
extract(a, ar, ag, ab)
|
||||
extract(b, br, bg, bb)
|
||||
@@ -39,24 +39,24 @@ proc satPlus(a, b: int): int {.inline.} =
|
||||
proc satMinus(a, b: int): int {.inline.} =
|
||||
result = a -% b
|
||||
if result < 0: result = 0
|
||||
|
||||
|
||||
proc `+`*(a, b: Color): Color =
|
||||
## adds two colors: This uses saturated artithmetic, so that each color
|
||||
## component cannot overflow (255 is used as a maximum).
|
||||
colorOp(satPlus)
|
||||
|
||||
|
||||
proc `-`*(a, b: Color): Color =
|
||||
## subtracts two colors: This uses saturated artithmetic, so that each color
|
||||
## component cannot overflow (255 is used as a maximum).
|
||||
colorOp(satMinus)
|
||||
|
||||
|
||||
proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
|
||||
## extracts the red/green/blue components of the color `a`.
|
||||
result.r = a.int shr 16 and 0xff
|
||||
result.g = a.int shr 8 and 0xff
|
||||
result.b = a.int and 0xff
|
||||
|
||||
proc intensity*(a: Color, f: float): Color =
|
||||
|
||||
proc intensity*(a: Color, f: float): Color =
|
||||
## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
|
||||
## dark) to 1.0 (full color intensity).
|
||||
var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
|
||||
@@ -66,7 +66,7 @@ proc intensity*(a: Color, f: float): Color =
|
||||
if g >% 255: g = 255
|
||||
if b >% 255: b = 255
|
||||
result = rawRGB(r, g, b)
|
||||
|
||||
|
||||
template mix*(a, b: Color, fn: expr): expr =
|
||||
## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
|
||||
## R, G, and B. This is a template because `fn` should be inlined and the
|
||||
@@ -79,7 +79,7 @@ template mix*(a, b: Color, fn: expr): expr =
|
||||
if y >% 255:
|
||||
y = if y < 0: 0 else: 255
|
||||
y
|
||||
|
||||
|
||||
(bind extract)(a, ar, ag, ab)
|
||||
(bind extract)(b, br, bg, bb)
|
||||
(bind rawRGB)(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
|
||||
@@ -226,7 +226,7 @@ const
|
||||
colWhiteSmoke* = Color(0xF5F5F5)
|
||||
colYellow* = Color(0xFFFF00)
|
||||
colYellowGreen* = Color(0x9ACD32)
|
||||
|
||||
|
||||
colorNames = [
|
||||
("aliceblue", colAliceBlue),
|
||||
("antiquewhite", colAntiqueWhite),
|
||||
@@ -369,24 +369,24 @@ const
|
||||
("yellow", colYellow),
|
||||
("yellowgreen", colYellowGreen)]
|
||||
|
||||
proc `$`*(c: Color): string =
|
||||
proc `$`*(c: Color): string =
|
||||
## converts a color into its textual representation. Example: ``#00FF00``.
|
||||
result = '#' & toHex(int(c), 6)
|
||||
|
||||
proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
|
||||
y: string): int =
|
||||
proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
|
||||
y: string): int =
|
||||
var a = 0
|
||||
var b = len(x) - 1
|
||||
while a <= b:
|
||||
while a <= b:
|
||||
var mid = (a + b) div 2
|
||||
var c = cmp(x[mid].name, y)
|
||||
if c < 0: a = mid + 1
|
||||
elif c > 0: b = mid - 1
|
||||
else: return mid
|
||||
result = - 1
|
||||
|
||||
proc parseColor*(name: string): Color =
|
||||
## parses `name` to a color value. If no valid color could be
|
||||
|
||||
proc parseColor*(name: string): Color =
|
||||
## parses `name` to a color value. If no valid color could be
|
||||
## parsed ``EInvalidValue`` is raised.
|
||||
if name[0] == '#':
|
||||
result = Color(parseHexInt(name))
|
||||
@@ -396,10 +396,10 @@ proc parseColor*(name: string): Color =
|
||||
result = colorNames[idx][1]
|
||||
|
||||
proc isColor*(name: string): bool =
|
||||
## returns true if `name` is a known color name or a hexadecimal color
|
||||
## returns true if `name` is a known color name or a hexadecimal color
|
||||
## prefixed with ``#``.
|
||||
if name[0] == '#':
|
||||
for i in 1 .. name.len-1:
|
||||
if name[0] == '#':
|
||||
for i in 1 .. name.len-1:
|
||||
if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false
|
||||
result = true
|
||||
else:
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import
|
||||
math
|
||||
|
||||
|
||||
const
|
||||
EPS = 1.0e-7 ## Epsilon used for float comparisons.
|
||||
|
||||
@@ -248,7 +248,7 @@ proc pow*(x, y: Complex): Complex =
|
||||
var r = y.re*theta + y.im*ln(rho)
|
||||
result.re = s*cos(r)
|
||||
result.im = s*sin(r)
|
||||
|
||||
|
||||
|
||||
proc sin*(z: Complex): Complex =
|
||||
## Returns the sine of `z`.
|
||||
@@ -387,7 +387,7 @@ when isMainModule:
|
||||
var one = (1.0,0.0)
|
||||
var tt = (10.0, 20.0)
|
||||
var ipi = (0.0, -PI)
|
||||
|
||||
|
||||
assert( a == a )
|
||||
assert( (a-a) == z )
|
||||
assert( (a+b) == z )
|
||||
@@ -403,7 +403,7 @@ when isMainModule:
|
||||
assert( conjugate(a) == (1.0, -2.0) )
|
||||
assert( sqrt(m1) == i )
|
||||
assert( exp(ipi) =~ m1 )
|
||||
|
||||
|
||||
assert( pow(a,b) =~ (-3.72999124927876, -1.68815826725068) )
|
||||
assert( pow(z,a) =~ (0.0, 0.0) )
|
||||
assert( pow(z,z) =~ (1.0, 0.0) )
|
||||
|
||||
@@ -18,7 +18,7 @@ when not defined(windows):
|
||||
|
||||
when defined(linux):
|
||||
import linux
|
||||
|
||||
|
||||
when defined(freebsd) or defined(macosx):
|
||||
{.emit:"#include <sys/types.h>".}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import strtabs, times
|
||||
|
||||
proc parseCookies*(s: string): StringTableRef =
|
||||
proc parseCookies*(s: string): StringTableRef =
|
||||
## parses cookies into a string table.
|
||||
result = newStringTable(modeCaseInsensitive)
|
||||
var i = 0
|
||||
@@ -31,7 +31,7 @@ proc parseCookies*(s: string): StringTableRef =
|
||||
proc setCookie*(key, value: string, domain = "", path = "",
|
||||
expires = "", noName = false,
|
||||
secure = false, httpOnly = false): string =
|
||||
## Creates a command in the format of
|
||||
## Creates a command in the format of
|
||||
## ``Set-Cookie: key=value; Domain=...; ...``
|
||||
result = ""
|
||||
if not noName: result.add("Set-Cookie: ")
|
||||
@@ -45,10 +45,10 @@ proc setCookie*(key, value: string, domain = "", path = "",
|
||||
proc setCookie*(key, value: string, expires: TimeInfo,
|
||||
domain = "", path = "", noName = false,
|
||||
secure = false, httpOnly = false): string =
|
||||
## Creates a command in the format of
|
||||
## Creates a command in the format of
|
||||
## ``Set-Cookie: key=value; Domain=...; ...``
|
||||
##
|
||||
## **Note:** UTC is assumed as the timezone for ``expires``.
|
||||
## **Note:** UTC is assumed as the timezone for ``expires``.
|
||||
return setCookie(key, value, domain, path,
|
||||
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"),
|
||||
noname, secure, httpOnly)
|
||||
@@ -61,7 +61,7 @@ when isMainModule:
|
||||
echo cookie
|
||||
let start = "Set-Cookie: test=value; Expires="
|
||||
assert cookie[0..start.high] == start
|
||||
|
||||
|
||||
let table = parseCookies("uid=1; kp=2")
|
||||
assert table["uid"] == "1"
|
||||
assert table["kp"] == "2"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Converts between different character encodings. On UNIX, this uses
|
||||
## Converts between different character encodings. On UNIX, this uses
|
||||
## the `iconv`:idx: library, on Windows the Windows API.
|
||||
|
||||
import os, parseutils, strutils
|
||||
@@ -41,173 +41,173 @@ when defined(windows):
|
||||
inc j
|
||||
result = i == a.len and j == b.len
|
||||
|
||||
const
|
||||
const
|
||||
winEncodings = [
|
||||
(1, "OEMCP"), # current OEM codepage
|
||||
(037, "IBM037"), # IBM EBCDIC US-Canada
|
||||
(437, "IBM437"), # OEM United States
|
||||
(500, "IBM500"), # IBM EBCDIC International
|
||||
(708, "ASMO-708"), # Arabic (ASMO 708)
|
||||
(709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4)
|
||||
(710, ""), # Arabic - Transparent Arabic
|
||||
(720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS)
|
||||
(737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS)
|
||||
(775, "ibm775"), # OEM Baltic; Baltic (DOS)
|
||||
(850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS)
|
||||
(852, "ibm852"), # OEM Latin 2; Central European (DOS)
|
||||
(855, "IBM855"), # OEM Cyrillic (primarily Russian)
|
||||
(857, "ibm857"), # OEM Turkish; Turkish (DOS)
|
||||
(858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol
|
||||
(860, "IBM860"), # OEM Portuguese; Portuguese (DOS)
|
||||
(861, "ibm861"), # OEM Icelandic; Icelandic (DOS)
|
||||
(862, "DOS-862"), # OEM Hebrew; Hebrew (DOS)
|
||||
(863, "IBM863"), # OEM French Canadian; French Canadian (DOS)
|
||||
(864, "IBM864"), # OEM Arabic; Arabic (864)
|
||||
(865, "IBM865"), # OEM Nordic; Nordic (DOS)
|
||||
(866, "cp866"), # OEM Russian; Cyrillic (DOS)
|
||||
(869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS)
|
||||
(870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
|
||||
(874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
|
||||
(875, "cp875"), # IBM EBCDIC Greek Modern
|
||||
(932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS)
|
||||
(936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
|
||||
(949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code)
|
||||
(950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
|
||||
(1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5)
|
||||
(1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System
|
||||
(1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
|
||||
(1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
|
||||
(1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
|
||||
(1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
|
||||
(1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
|
||||
(1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
|
||||
(1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
|
||||
(1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
|
||||
(1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
|
||||
(1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
|
||||
(1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
|
||||
(1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications
|
||||
(1250, "windows-1250"), # ANSI Central European; Central European (Windows)
|
||||
(1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows)
|
||||
(1252, "windows-1252"), # ANSI Latin 1; Western European (Windows)
|
||||
(1253, "windows-1253"), # ANSI Greek; Greek (Windows)
|
||||
(1254, "windows-1254"), # ANSI Turkish; Turkish (Windows)
|
||||
(1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows)
|
||||
(1256, "windows-1256"), # ANSI Arabic; Arabic (Windows)
|
||||
(1257, "windows-1257"), # ANSI Baltic; Baltic (Windows)
|
||||
(1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
|
||||
(037, "IBM037"), # IBM EBCDIC US-Canada
|
||||
(437, "IBM437"), # OEM United States
|
||||
(500, "IBM500"), # IBM EBCDIC International
|
||||
(708, "ASMO-708"), # Arabic (ASMO 708)
|
||||
(709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4)
|
||||
(710, ""), # Arabic - Transparent Arabic
|
||||
(720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS)
|
||||
(737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS)
|
||||
(775, "ibm775"), # OEM Baltic; Baltic (DOS)
|
||||
(850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS)
|
||||
(852, "ibm852"), # OEM Latin 2; Central European (DOS)
|
||||
(855, "IBM855"), # OEM Cyrillic (primarily Russian)
|
||||
(857, "ibm857"), # OEM Turkish; Turkish (DOS)
|
||||
(858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol
|
||||
(860, "IBM860"), # OEM Portuguese; Portuguese (DOS)
|
||||
(861, "ibm861"), # OEM Icelandic; Icelandic (DOS)
|
||||
(862, "DOS-862"), # OEM Hebrew; Hebrew (DOS)
|
||||
(863, "IBM863"), # OEM French Canadian; French Canadian (DOS)
|
||||
(864, "IBM864"), # OEM Arabic; Arabic (864)
|
||||
(865, "IBM865"), # OEM Nordic; Nordic (DOS)
|
||||
(866, "cp866"), # OEM Russian; Cyrillic (DOS)
|
||||
(869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS)
|
||||
(870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
|
||||
(874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
|
||||
(875, "cp875"), # IBM EBCDIC Greek Modern
|
||||
(932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS)
|
||||
(936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
|
||||
(949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code)
|
||||
(950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
|
||||
(1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5)
|
||||
(1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System
|
||||
(1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
|
||||
(1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
|
||||
(1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
|
||||
(1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
|
||||
(1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
|
||||
(1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
|
||||
(1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
|
||||
(1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
|
||||
(1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
|
||||
(1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
|
||||
(1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
|
||||
(1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications
|
||||
(1250, "windows-1250"), # ANSI Central European; Central European (Windows)
|
||||
(1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows)
|
||||
(1252, "windows-1252"), # ANSI Latin 1; Western European (Windows)
|
||||
(1253, "windows-1253"), # ANSI Greek; Greek (Windows)
|
||||
(1254, "windows-1254"), # ANSI Turkish; Turkish (Windows)
|
||||
(1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows)
|
||||
(1256, "windows-1256"), # ANSI Arabic; Arabic (Windows)
|
||||
(1257, "windows-1257"), # ANSI Baltic; Baltic (Windows)
|
||||
(1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
|
||||
|
||||
(1250, "cp-1250"), # ANSI Central European; Central European (Windows)
|
||||
(1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows)
|
||||
(1252, "cp-1252"), # ANSI Latin 1; Western European (Windows)
|
||||
(1253, "cp-1253"), # ANSI Greek; Greek (Windows)
|
||||
(1254, "cp-1254"), # ANSI Turkish; Turkish (Windows)
|
||||
(1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows)
|
||||
(1256, "cp-1256"), # ANSI Arabic; Arabic (Windows)
|
||||
(1257, "cp-1257"), # ANSI Baltic; Baltic (Windows)
|
||||
(1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
|
||||
(1250, "cp-1250"), # ANSI Central European; Central European (Windows)
|
||||
(1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows)
|
||||
(1252, "cp-1252"), # ANSI Latin 1; Western European (Windows)
|
||||
(1253, "cp-1253"), # ANSI Greek; Greek (Windows)
|
||||
(1254, "cp-1254"), # ANSI Turkish; Turkish (Windows)
|
||||
(1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows)
|
||||
(1256, "cp-1256"), # ANSI Arabic; Arabic (Windows)
|
||||
(1257, "cp-1257"), # ANSI Baltic; Baltic (Windows)
|
||||
(1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
|
||||
|
||||
(1361, "Johab"), # Korean (Johab)
|
||||
(10000, "macintosh"), # MAC Roman; Western European (Mac)
|
||||
(10001, "x-mac-japanese"), # Japanese (Mac)
|
||||
(10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
|
||||
(10003, "x-mac-korean"), # Korean (Mac)
|
||||
(10004, "x-mac-arabic"), # Arabic (Mac)
|
||||
(10005, "x-mac-hebrew"), # Hebrew (Mac)
|
||||
(10006, "x-mac-greek"), # Greek (Mac)
|
||||
(10007, "x-mac-cyrillic"), # Cyrillic (Mac)
|
||||
(10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
|
||||
(10010, "x-mac-romanian"), # Romanian (Mac)
|
||||
(10017, "x-mac-ukrainian"), # Ukrainian (Mac)
|
||||
(10021, "x-mac-thai"), # Thai (Mac)
|
||||
(10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac)
|
||||
(10079, "x-mac-icelandic"), # Icelandic (Mac)
|
||||
(10081, "x-mac-turkish"), # Turkish (Mac)
|
||||
(10082, "x-mac-croatian"), # Croatian (Mac)
|
||||
(12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications
|
||||
(12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications
|
||||
(20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS)
|
||||
(20001, "x-cp20001"), # TCA Taiwan
|
||||
(20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten)
|
||||
(20003, "x-cp20003"), # IBM5550 Taiwan
|
||||
(20004, "x-cp20004"), # TeleText Taiwan
|
||||
(20005, "x-cp20005"), # Wang Taiwan
|
||||
(20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
|
||||
(20106, "x-IA5-German"), # IA5 German (7-bit)
|
||||
(20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit)
|
||||
(20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit)
|
||||
(20127, "us-ascii"), # US-ASCII (7-bit)
|
||||
(20261, "x-cp20261"), # T.61
|
||||
(20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent
|
||||
(20273, "IBM273"), # IBM EBCDIC Germany
|
||||
(20277, "IBM277"), # IBM EBCDIC Denmark-Norway
|
||||
(20278, "IBM278"), # IBM EBCDIC Finland-Sweden
|
||||
(20280, "IBM280"), # IBM EBCDIC Italy
|
||||
(20284, "IBM284"), # IBM EBCDIC Latin America-Spain
|
||||
(20285, "IBM285"), # IBM EBCDIC United Kingdom
|
||||
(20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended
|
||||
(20297, "IBM297"), # IBM EBCDIC France
|
||||
(20420, "IBM420"), # IBM EBCDIC Arabic
|
||||
(20423, "IBM423"), # IBM EBCDIC Greek
|
||||
(20424, "IBM424"), # IBM EBCDIC Hebrew
|
||||
(20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended
|
||||
(20838, "IBM-Thai"), # IBM EBCDIC Thai
|
||||
(20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R)
|
||||
(20871, "IBM871"), # IBM EBCDIC Icelandic
|
||||
(20880, "IBM880"), # IBM EBCDIC Cyrillic Russian
|
||||
(20905, "IBM905"), # IBM EBCDIC Turkish
|
||||
(20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
|
||||
(20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990)
|
||||
(20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
|
||||
(20949, "x-cp20949"), # Korean Wansung
|
||||
(21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian
|
||||
(21027, ""), # (deprecated)
|
||||
(21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U)
|
||||
(28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO)
|
||||
(28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO)
|
||||
(28593, "iso-8859-3"), # ISO 8859-3 Latin 3
|
||||
(28594, "iso-8859-4"), # ISO 8859-4 Baltic
|
||||
(28595, "iso-8859-5"), # ISO 8859-5 Cyrillic
|
||||
(28596, "iso-8859-6"), # ISO 8859-6 Arabic
|
||||
(28597, "iso-8859-7"), # ISO 8859-7 Greek
|
||||
(28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
|
||||
(28599, "iso-8859-9"), # ISO 8859-9 Turkish
|
||||
(28603, "iso-8859-13"), # ISO 8859-13 Estonian
|
||||
(28605, "iso-8859-15"), # ISO 8859-15 Latin 9
|
||||
(29001, "x-Europa"), # Europa 3
|
||||
(38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
|
||||
(50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
|
||||
(50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
|
||||
(50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
|
||||
(50225, "iso-2022-kr"), # ISO 2022 Korean
|
||||
(50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
|
||||
(50229, ""), # ISO 2022 Traditional Chinese
|
||||
(50930, ""), # EBCDIC Japanese (Katakana) Extended
|
||||
(50931, ""), # EBCDIC US-Canada and Japanese
|
||||
(50933, ""), # EBCDIC Korean Extended and Korean
|
||||
(50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese
|
||||
(50936, ""), # EBCDIC Simplified Chinese
|
||||
(50937, ""), # EBCDIC US-Canada and Traditional Chinese
|
||||
(50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese
|
||||
(51932, "euc-jp"), # EUC Japanese
|
||||
(51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC)
|
||||
(51949, "euc-kr"), # EUC Korean
|
||||
(51950, ""), # EUC Traditional Chinese
|
||||
(52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
|
||||
(54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
|
||||
(57002, "x-iscii-de"), # ISCII Devanagari
|
||||
(57003, "x-iscii-be"), # ISCII Bengali
|
||||
(57004, "x-iscii-ta"), # ISCII Tamil
|
||||
(57005, "x-iscii-te"), # ISCII Telugu
|
||||
(57006, "x-iscii-as"), # ISCII Assamese
|
||||
(57007, "x-iscii-or"), # ISCII Oriya
|
||||
(57008, "x-iscii-ka"), # ISCII Kannada
|
||||
(57009, "x-iscii-ma"), # ISCII Malayalam
|
||||
(57010, "x-iscii-gu"), # ISCII Gujarati
|
||||
(57011, "x-iscii-pa"), # ISCII Punjabi
|
||||
(65000, "utf-7"), # Unicode (UTF-7)
|
||||
(65001, "utf-8")] # Unicode (UTF-8)
|
||||
|
||||
(1361, "Johab"), # Korean (Johab)
|
||||
(10000, "macintosh"), # MAC Roman; Western European (Mac)
|
||||
(10001, "x-mac-japanese"), # Japanese (Mac)
|
||||
(10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
|
||||
(10003, "x-mac-korean"), # Korean (Mac)
|
||||
(10004, "x-mac-arabic"), # Arabic (Mac)
|
||||
(10005, "x-mac-hebrew"), # Hebrew (Mac)
|
||||
(10006, "x-mac-greek"), # Greek (Mac)
|
||||
(10007, "x-mac-cyrillic"), # Cyrillic (Mac)
|
||||
(10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
|
||||
(10010, "x-mac-romanian"), # Romanian (Mac)
|
||||
(10017, "x-mac-ukrainian"), # Ukrainian (Mac)
|
||||
(10021, "x-mac-thai"), # Thai (Mac)
|
||||
(10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac)
|
||||
(10079, "x-mac-icelandic"), # Icelandic (Mac)
|
||||
(10081, "x-mac-turkish"), # Turkish (Mac)
|
||||
(10082, "x-mac-croatian"), # Croatian (Mac)
|
||||
(12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications
|
||||
(12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications
|
||||
(20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS)
|
||||
(20001, "x-cp20001"), # TCA Taiwan
|
||||
(20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten)
|
||||
(20003, "x-cp20003"), # IBM5550 Taiwan
|
||||
(20004, "x-cp20004"), # TeleText Taiwan
|
||||
(20005, "x-cp20005"), # Wang Taiwan
|
||||
(20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
|
||||
(20106, "x-IA5-German"), # IA5 German (7-bit)
|
||||
(20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit)
|
||||
(20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit)
|
||||
(20127, "us-ascii"), # US-ASCII (7-bit)
|
||||
(20261, "x-cp20261"), # T.61
|
||||
(20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent
|
||||
(20273, "IBM273"), # IBM EBCDIC Germany
|
||||
(20277, "IBM277"), # IBM EBCDIC Denmark-Norway
|
||||
(20278, "IBM278"), # IBM EBCDIC Finland-Sweden
|
||||
(20280, "IBM280"), # IBM EBCDIC Italy
|
||||
(20284, "IBM284"), # IBM EBCDIC Latin America-Spain
|
||||
(20285, "IBM285"), # IBM EBCDIC United Kingdom
|
||||
(20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended
|
||||
(20297, "IBM297"), # IBM EBCDIC France
|
||||
(20420, "IBM420"), # IBM EBCDIC Arabic
|
||||
(20423, "IBM423"), # IBM EBCDIC Greek
|
||||
(20424, "IBM424"), # IBM EBCDIC Hebrew
|
||||
(20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended
|
||||
(20838, "IBM-Thai"), # IBM EBCDIC Thai
|
||||
(20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R)
|
||||
(20871, "IBM871"), # IBM EBCDIC Icelandic
|
||||
(20880, "IBM880"), # IBM EBCDIC Cyrillic Russian
|
||||
(20905, "IBM905"), # IBM EBCDIC Turkish
|
||||
(20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
|
||||
(20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990)
|
||||
(20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
|
||||
(20949, "x-cp20949"), # Korean Wansung
|
||||
(21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian
|
||||
(21027, ""), # (deprecated)
|
||||
(21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U)
|
||||
(28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO)
|
||||
(28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO)
|
||||
(28593, "iso-8859-3"), # ISO 8859-3 Latin 3
|
||||
(28594, "iso-8859-4"), # ISO 8859-4 Baltic
|
||||
(28595, "iso-8859-5"), # ISO 8859-5 Cyrillic
|
||||
(28596, "iso-8859-6"), # ISO 8859-6 Arabic
|
||||
(28597, "iso-8859-7"), # ISO 8859-7 Greek
|
||||
(28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
|
||||
(28599, "iso-8859-9"), # ISO 8859-9 Turkish
|
||||
(28603, "iso-8859-13"), # ISO 8859-13 Estonian
|
||||
(28605, "iso-8859-15"), # ISO 8859-15 Latin 9
|
||||
(29001, "x-Europa"), # Europa 3
|
||||
(38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
|
||||
(50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
|
||||
(50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
|
||||
(50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
|
||||
(50225, "iso-2022-kr"), # ISO 2022 Korean
|
||||
(50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
|
||||
(50229, ""), # ISO 2022 Traditional Chinese
|
||||
(50930, ""), # EBCDIC Japanese (Katakana) Extended
|
||||
(50931, ""), # EBCDIC US-Canada and Japanese
|
||||
(50933, ""), # EBCDIC Korean Extended and Korean
|
||||
(50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese
|
||||
(50936, ""), # EBCDIC Simplified Chinese
|
||||
(50937, ""), # EBCDIC US-Canada and Traditional Chinese
|
||||
(50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese
|
||||
(51932, "euc-jp"), # EUC Japanese
|
||||
(51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC)
|
||||
(51949, "euc-kr"), # EUC Korean
|
||||
(51950, ""), # EUC Traditional Chinese
|
||||
(52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
|
||||
(54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
|
||||
(57002, "x-iscii-de"), # ISCII Devanagari
|
||||
(57003, "x-iscii-be"), # ISCII Bengali
|
||||
(57004, "x-iscii-ta"), # ISCII Tamil
|
||||
(57005, "x-iscii-te"), # ISCII Telugu
|
||||
(57006, "x-iscii-as"), # ISCII Assamese
|
||||
(57007, "x-iscii-or"), # ISCII Oriya
|
||||
(57008, "x-iscii-ka"), # ISCII Kannada
|
||||
(57009, "x-iscii-ma"), # ISCII Malayalam
|
||||
(57010, "x-iscii-gu"), # ISCII Gujarati
|
||||
(57011, "x-iscii-pa"), # ISCII Punjabi
|
||||
(65000, "utf-7"), # Unicode (UTF-7)
|
||||
(65001, "utf-8")] # Unicode (UTF-8)
|
||||
|
||||
when false:
|
||||
# not needed yet:
|
||||
type
|
||||
@@ -219,22 +219,22 @@ when defined(windows):
|
||||
|
||||
proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
|
||||
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
||||
|
||||
|
||||
proc nameToCodePage(name: string): CodePage =
|
||||
var nameAsInt: int
|
||||
if parseInt(name, nameAsInt) == 0: nameAsInt = -1
|
||||
for no, na in items(winEncodings):
|
||||
if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
|
||||
result = CodePage(-1)
|
||||
|
||||
|
||||
proc codePageToName(c: CodePage): string =
|
||||
for no, na in items(winEncodings):
|
||||
if no == int(c):
|
||||
return if na.len != 0: na else: $no
|
||||
result = ""
|
||||
|
||||
|
||||
proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
||||
|
||||
|
||||
proc multiByteToWideChar(
|
||||
codePage: CodePage,
|
||||
dwFlags: int32,
|
||||
@@ -254,7 +254,7 @@ when defined(windows):
|
||||
lpDefaultChar: cstring=nil,
|
||||
lpUsedDefaultChar: pointer=nil): cint {.
|
||||
stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
|
||||
|
||||
|
||||
else:
|
||||
when defined(haiku):
|
||||
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
|
||||
@@ -292,31 +292,31 @@ else:
|
||||
proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer,
|
||||
outbuf: var cstring, outbytesLeft: var int): int {.
|
||||
importc: prefix & "iconv", cdecl, dynlib: iconvDll.}
|
||||
|
||||
|
||||
proc getCurrentEncoding*(): string =
|
||||
## retrieves the current encoding. On Unix, always "UTF-8" is returned.
|
||||
when defined(windows):
|
||||
result = codePageToName(getACP())
|
||||
else:
|
||||
result = "UTF-8"
|
||||
|
||||
|
||||
proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
|
||||
## opens a converter that can convert from `srcEncoding` to `destEncoding`.
|
||||
## Raises `EIO` if it cannot fulfill the request.
|
||||
when not defined(windows):
|
||||
result = iconvOpen(destEncoding, srcEncoding)
|
||||
if result == nil:
|
||||
raise newException(EncodingError,
|
||||
"cannot create encoding converter from " &
|
||||
raise newException(EncodingError,
|
||||
"cannot create encoding converter from " &
|
||||
srcEncoding & " to " & destEncoding)
|
||||
else:
|
||||
result.dest = nameToCodePage(destEncoding)
|
||||
result.src = nameToCodePage(srcEncoding)
|
||||
if int(result.dest) == -1:
|
||||
raise newException(EncodingError,
|
||||
raise newException(EncodingError,
|
||||
"cannot find encoding " & destEncoding)
|
||||
if int(result.src) == -1:
|
||||
raise newException(EncodingError,
|
||||
raise newException(EncodingError,
|
||||
"cannot find encoding " & srcEncoding)
|
||||
|
||||
proc close*(c: EncodingConverter) =
|
||||
@@ -328,7 +328,7 @@ when defined(windows):
|
||||
proc convert*(c: EncodingConverter, s: string): string =
|
||||
## converts `s` to `destEncoding` that was given to the converter `c`. It
|
||||
## assumed that `s` is in `srcEncoding`.
|
||||
|
||||
|
||||
# special case: empty string: needed because MultiByteToWideChar
|
||||
# return 0 in case of error:
|
||||
if s.len == 0: return ""
|
||||
@@ -336,21 +336,21 @@ when defined(windows):
|
||||
var cap = s.len + s.len shr 2
|
||||
result = newStringOfCap(cap*2)
|
||||
# convert to utf-16 LE
|
||||
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
lpMultiByteStr = cstring(s),
|
||||
cbMultiByte = cint(s.len),
|
||||
lpWideCharStr = cstring(result),
|
||||
cchWideChar = cint(cap))
|
||||
if m == 0:
|
||||
if m == 0:
|
||||
# try again; ask for capacity:
|
||||
cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
lpMultiByteStr = cstring(s),
|
||||
cbMultiByte = cint(s.len),
|
||||
lpWideCharStr = nil,
|
||||
cchWideChar = cint(0))
|
||||
# and do the conversion properly:
|
||||
result = newStringOfCap(cap*2)
|
||||
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
lpMultiByteStr = cstring(s),
|
||||
cbMultiByte = cint(s.len),
|
||||
lpWideCharStr = cstring(result),
|
||||
@@ -361,7 +361,7 @@ when defined(windows):
|
||||
setLen(result, m*2)
|
||||
else:
|
||||
assert(false) # cannot happen
|
||||
|
||||
|
||||
# if already utf-16 LE, no further need to do something:
|
||||
if int(c.dest) == 1200: return
|
||||
# otherwise the fun starts again:
|
||||
@@ -428,7 +428,7 @@ else:
|
||||
outLen = len(result) - offset
|
||||
else:
|
||||
raiseOSError(lerr.OSErrorCode)
|
||||
# iconv has a buffer that needs flushing, specially if the last char is
|
||||
# iconv has a buffer that needs flushing, specially if the last char is
|
||||
# not '\0'
|
||||
discard iconv(c, nil, nil, dst, outLen)
|
||||
if iconvres == cint(-1) and errno == E2BIG:
|
||||
@@ -441,7 +441,7 @@ else:
|
||||
# trim output buffer
|
||||
setLen(result, len(result) - outLen)
|
||||
|
||||
proc convert*(s: string, destEncoding = "UTF-8",
|
||||
proc convert*(s: string, destEncoding = "UTF-8",
|
||||
srcEncoding = "CP1252"): string =
|
||||
## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`.
|
||||
## This opens a converter, uses it and closes it again and is thus more
|
||||
|
||||
@@ -24,7 +24,7 @@ proc swapEndian64*(outp, inp: pointer) =
|
||||
o[6] = i[1]
|
||||
o[7] = i[0]
|
||||
|
||||
proc swapEndian32*(outp, inp: pointer) =
|
||||
proc swapEndian32*(outp, inp: pointer) =
|
||||
## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
|
||||
## contain at least 4 bytes.
|
||||
var i = cast[cstring](inp)
|
||||
@@ -34,7 +34,7 @@ proc swapEndian32*(outp, inp: pointer) =
|
||||
o[2] = i[1]
|
||||
o[3] = i[0]
|
||||
|
||||
proc swapEndian16*(outp, inp: pointer) =
|
||||
proc swapEndian16*(outp, inp: pointer) =
|
||||
## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
|
||||
## contain at least 2 bytes.
|
||||
var
|
||||
@@ -50,7 +50,7 @@ when system.cpuEndian == bigEndian:
|
||||
proc bigEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
|
||||
proc bigEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
|
||||
proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2)
|
||||
else:
|
||||
else:
|
||||
proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
|
||||
proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
|
||||
proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2)
|
||||
|
||||
@@ -18,6 +18,6 @@ const magicIdentSeparatorRuneByteWidth* = 3
|
||||
|
||||
# Used by pure/hashes.nim, and the compiler parsing
|
||||
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } =
|
||||
result = cs[i] == '\226' and
|
||||
result = cs[i] == '\226' and
|
||||
cs[i + 1] == '\128' and
|
||||
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## :Author: Alex Mitchell
|
||||
##
|
||||
## This module implements an event system that is not dependent on external
|
||||
## graphical toolkits. It was originally called ``NimEE`` because
|
||||
## graphical toolkits. It was originally called ``NimEE`` because
|
||||
## it was inspired by Python's PyEE module. There are two ways you can use
|
||||
## events: one is a python-inspired way; the other is more of a C-style way.
|
||||
##
|
||||
@@ -23,7 +23,7 @@
|
||||
## # Python way
|
||||
## ee.on("EventName", handleevent)
|
||||
## ee.emit("EventName", genericargs)
|
||||
##
|
||||
##
|
||||
## # C/Java way
|
||||
## # Declare a type
|
||||
## type
|
||||
@@ -45,7 +45,7 @@ type
|
||||
|
||||
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
|
||||
TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
|
||||
|
||||
|
||||
proc initEventHandler*(name: string): EventHandler =
|
||||
## Initializes an EventHandler with the specified name and returns it.
|
||||
result.handlers = @[]
|
||||
@@ -61,7 +61,7 @@ proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.
|
||||
if fn == handler.handlers[i]:
|
||||
handler.handlers.del(i)
|
||||
break
|
||||
|
||||
|
||||
proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool =
|
||||
## Checks if a callback is registered to this event handler.
|
||||
return handler.handlers.contains(fn)
|
||||
@@ -86,8 +86,8 @@ proc on*(emitter: var EventEmitter, event: string, fn: proc(e: EventArgs) {.clos
|
||||
emitter.s.add(eh)
|
||||
else:
|
||||
addHandler(emitter.s[i], fn)
|
||||
|
||||
proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler,
|
||||
|
||||
proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler,
|
||||
args: EventArgs) =
|
||||
## Fires an event handler with specified event arguments.
|
||||
for fn in items(eventhandler.handlers): fn(args)
|
||||
|
||||
@@ -30,7 +30,7 @@ type
|
||||
fd: cint
|
||||
handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
|
||||
targets: Table[cint, string]
|
||||
|
||||
|
||||
MonitorEventType* = enum ## Monitor event type
|
||||
MonitorAccess, ## File was accessed.
|
||||
MonitorAttrib, ## Metadata changed.
|
||||
@@ -44,7 +44,7 @@ type
|
||||
MonitorMoved, ## File was moved.
|
||||
MonitorOpen, ## File was opened.
|
||||
MonitorAll ## Filter for all event types.
|
||||
|
||||
|
||||
MonitorEvent* = object
|
||||
case kind*: MonitorEventType ## Type of the event.
|
||||
of MonitorMoveSelf, MonitorMoved:
|
||||
@@ -77,7 +77,7 @@ proc add*(monitor: FSMonitor, target: string,
|
||||
## Adds ``target`` which may be a directory or a file to the list of
|
||||
## watched paths of ``monitor``.
|
||||
## You can specify the events to report using the ``filters`` parameter.
|
||||
|
||||
|
||||
var INFilter = -1
|
||||
for f in filters:
|
||||
case f
|
||||
@@ -93,7 +93,7 @@ proc add*(monitor: FSMonitor, target: string,
|
||||
of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO
|
||||
of MonitorOpen: INFilter = INFilter and IN_OPEN
|
||||
of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS
|
||||
|
||||
|
||||
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
|
||||
if result < 0:
|
||||
raiseOSError(osLastError())
|
||||
@@ -125,13 +125,13 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
mev.name = $cstr
|
||||
else:
|
||||
mev.name = ""
|
||||
|
||||
if (event.mask.int and IN_MOVED_FROM) != 0:
|
||||
|
||||
if (event.mask.int and IN_MOVED_FROM) != 0:
|
||||
# Moved from event, add to m's collection
|
||||
movedFrom.add(event.cookie.cint, (mev.wd, mev.name))
|
||||
inc(i, sizeof(INotifyEvent) + event.len.int)
|
||||
continue
|
||||
elif (event.mask.int and IN_MOVED_TO) != 0:
|
||||
elif (event.mask.int and IN_MOVED_TO) != 0:
|
||||
mev.kind = MonitorMoved
|
||||
assert movedFrom.hasKey(event.cookie.cint)
|
||||
# Find the MovedFrom event.
|
||||
@@ -141,23 +141,23 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
movedFrom.del(event.cookie.cint)
|
||||
elif (event.mask.int and IN_ACCESS) != 0: mev.kind = MonitorAccess
|
||||
elif (event.mask.int and IN_ATTRIB) != 0: mev.kind = MonitorAttrib
|
||||
elif (event.mask.int and IN_CLOSE_WRITE) != 0:
|
||||
elif (event.mask.int and IN_CLOSE_WRITE) != 0:
|
||||
mev.kind = MonitorCloseWrite
|
||||
elif (event.mask.int and IN_CLOSE_NOWRITE) != 0:
|
||||
elif (event.mask.int and IN_CLOSE_NOWRITE) != 0:
|
||||
mev.kind = MonitorCloseNoWrite
|
||||
elif (event.mask.int and IN_CREATE) != 0: mev.kind = MonitorCreate
|
||||
elif (event.mask.int and IN_DELETE) != 0:
|
||||
elif (event.mask.int and IN_DELETE) != 0:
|
||||
mev.kind = MonitorDelete
|
||||
elif (event.mask.int and IN_DELETE_SELF) != 0:
|
||||
elif (event.mask.int and IN_DELETE_SELF) != 0:
|
||||
mev.kind = MonitorDeleteSelf
|
||||
elif (event.mask.int and IN_MODIFY) != 0: mev.kind = MonitorModify
|
||||
elif (event.mask.int and IN_MOVE_SELF) != 0:
|
||||
elif (event.mask.int and IN_MOVE_SELF) != 0:
|
||||
mev.kind = MonitorMoveSelf
|
||||
elif (event.mask.int and IN_OPEN) != 0: mev.kind = MonitorOpen
|
||||
|
||||
|
||||
if mev.kind != MonitorMoved:
|
||||
mev.fullname = ""
|
||||
|
||||
|
||||
result.add(mev)
|
||||
inc(i, sizeof(INotifyEvent) + event.len.int)
|
||||
|
||||
@@ -211,7 +211,7 @@ when not defined(testing) and isMainModule:
|
||||
echo("Name is ", ev.name)
|
||||
else:
|
||||
echo("Name ", ev.name, " fullname ", ev.fullName))
|
||||
|
||||
|
||||
while true:
|
||||
if not disp.poll(): break
|
||||
main()
|
||||
|
||||
@@ -142,7 +142,7 @@ when false:
|
||||
if meth == reqPost:
|
||||
# get from client and post to CGI program:
|
||||
var buf = alloc(contentLength)
|
||||
if recv(client, buf, contentLength) != contentLength:
|
||||
if recv(client, buf, contentLength) != contentLength:
|
||||
dealloc(buf)
|
||||
raiseOSError()
|
||||
var inp = process.inputStream
|
||||
@@ -177,7 +177,7 @@ when false:
|
||||
else:
|
||||
path = "." & data[1]
|
||||
# path starts with "/", by adding "." in front of it we serve files from cwd
|
||||
|
||||
|
||||
if cmpIgnoreCase(data[0], "GET") == 0:
|
||||
if q >= 0:
|
||||
cgi = true
|
||||
@@ -218,12 +218,12 @@ type
|
||||
headers*: StringTableRef ## headers with which the client made the request
|
||||
body*: string ## only set with POST requests
|
||||
ip*: string ## ip address of the requesting client
|
||||
|
||||
|
||||
PAsyncHTTPServer* = ref AsyncHTTPServer
|
||||
AsyncHTTPServer = object of Server
|
||||
asyncSocket: AsyncSocket
|
||||
{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
|
||||
|
||||
|
||||
proc open*(s: var Server, port = Port(80), reuseAddr = false) =
|
||||
## creates a new server at port `port`. If ``port == 0`` a free port is
|
||||
## acquired that can be accessed later by the ``port`` proc.
|
||||
@@ -262,7 +262,7 @@ proc next*(s: var Server) =
|
||||
var data = ""
|
||||
s.client.readLine(data)
|
||||
if data == "":
|
||||
# Socket disconnected
|
||||
# Socket disconnected
|
||||
s.client.close()
|
||||
next(s)
|
||||
return
|
||||
@@ -283,9 +283,9 @@ proc next*(s: var Server) =
|
||||
s.client.close()
|
||||
next(s)
|
||||
return
|
||||
|
||||
|
||||
var i = skipWhitespace(data)
|
||||
if skipIgnoreCase(data, "GET") > 0:
|
||||
if skipIgnoreCase(data, "GET") > 0:
|
||||
s.reqMethod = "GET"
|
||||
inc(i, 3)
|
||||
elif skipIgnoreCase(data, "POST") > 0:
|
||||
@@ -296,7 +296,7 @@ proc next*(s: var Server) =
|
||||
s.client.close()
|
||||
next(s)
|
||||
return
|
||||
|
||||
|
||||
if s.reqMethod == "POST":
|
||||
# Check for Expect header
|
||||
if s.headers.hasKey("Expect"):
|
||||
@@ -304,7 +304,7 @@ proc next*(s: var Server) =
|
||||
s.client.sendStatus("100 Continue")
|
||||
else:
|
||||
s.client.sendStatus("417 Expectation Failed")
|
||||
|
||||
|
||||
# Read the body
|
||||
# - Check for Content-length header
|
||||
if s.headers.hasKey("Content-Length"):
|
||||
@@ -340,13 +340,13 @@ proc next*(s: var Server) =
|
||||
s.client.close()
|
||||
next(s)
|
||||
return
|
||||
|
||||
|
||||
var L = skipWhitespace(data, i)
|
||||
inc(i, L)
|
||||
# XXX we ignore "HTTP/1.1" etc. for now here
|
||||
var query = 0
|
||||
var last = i
|
||||
while last < data.len and data[last] notin Whitespace:
|
||||
while last < data.len and data[last] notin Whitespace:
|
||||
if data[last] == '?' and query == 0: query = last
|
||||
inc(last)
|
||||
if query > 0:
|
||||
@@ -360,7 +360,7 @@ proc close*(s: Server) =
|
||||
## closes the server (and the socket the server uses).
|
||||
close(s.socket)
|
||||
|
||||
proc run*(handleRequest: proc (client: Socket,
|
||||
proc run*(handleRequest: proc (client: Socket,
|
||||
path, query: string): bool {.closure.},
|
||||
port = Port(80)) =
|
||||
## encapsulates the server object and main loop
|
||||
@@ -388,7 +388,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
var data = ""
|
||||
s.client.readLine(data)
|
||||
if data == "":
|
||||
# Socket disconnected
|
||||
# Socket disconnected
|
||||
s.client.close()
|
||||
return
|
||||
var header = ""
|
||||
@@ -408,9 +408,9 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
else:
|
||||
s.client.close()
|
||||
return
|
||||
|
||||
|
||||
var i = skipWhitespace(data)
|
||||
if skipIgnoreCase(data, "GET") > 0:
|
||||
if skipIgnoreCase(data, "GET") > 0:
|
||||
s.reqMethod = "GET"
|
||||
inc(i, 3)
|
||||
elif skipIgnoreCase(data, "POST") > 0:
|
||||
@@ -420,7 +420,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
unimplemented(s.client)
|
||||
s.client.close()
|
||||
return
|
||||
|
||||
|
||||
if s.reqMethod == "POST":
|
||||
# Check for Expect header
|
||||
if s.headers.hasKey("Expect"):
|
||||
@@ -428,7 +428,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
s.client.sendStatus("100 Continue")
|
||||
else:
|
||||
s.client.sendStatus("417 Expectation Failed")
|
||||
|
||||
|
||||
# Read the body
|
||||
# - Check for Content-length header
|
||||
if s.headers.hasKey("Content-Length"):
|
||||
@@ -460,13 +460,13 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
badRequest(s.client)
|
||||
s.client.close()
|
||||
return
|
||||
|
||||
|
||||
var L = skipWhitespace(data, i)
|
||||
inc(i, L)
|
||||
# XXX we ignore "HTTP/1.1" etc. for now here
|
||||
var query = 0
|
||||
var last = i
|
||||
while last < data.len and data[last] notin Whitespace:
|
||||
while last < data.len and data[last] notin Whitespace:
|
||||
if data[last] == '?' and query == 0: query = last
|
||||
inc(last)
|
||||
if query > 0:
|
||||
@@ -476,7 +476,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
s.query = ""
|
||||
s.path = data.substr(i, last-1)
|
||||
|
||||
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
|
||||
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
|
||||
path, query: string): bool {.closure, gcsafe.},
|
||||
port = Port(80), address = "",
|
||||
reuseAddr = false): PAsyncHTTPServer =
|
||||
@@ -492,14 +492,14 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Soc
|
||||
if quit: capturedRet.asyncSocket.close()
|
||||
if reuseAddr:
|
||||
capturedRet.asyncSocket.setSockOpt(OptReuseAddr, true)
|
||||
|
||||
|
||||
capturedRet.asyncSocket.bindAddr(port, address)
|
||||
capturedRet.asyncSocket.listen()
|
||||
if port == Port(0):
|
||||
capturedRet.port = getSockName(capturedRet.asyncSocket)
|
||||
else:
|
||||
capturedRet.port = port
|
||||
|
||||
|
||||
capturedRet.client = invalidSocket
|
||||
capturedRet.reqMethod = ""
|
||||
capturedRet.body = ""
|
||||
@@ -524,11 +524,11 @@ when not defined(testing) and isMainModule:
|
||||
echo("httpserver running on port ", s.port)
|
||||
while true:
|
||||
next(s)
|
||||
|
||||
|
||||
inc(counter)
|
||||
s.client.send("Hello, Andreas, for the $#th time. $# ? $#" % [
|
||||
$counter, s.path, s.query] & wwwNL)
|
||||
|
||||
|
||||
close(s.client)
|
||||
close(s)
|
||||
|
||||
|
||||
@@ -18,15 +18,15 @@ include "system/inclrtl"
|
||||
import parseutils, strutils
|
||||
|
||||
proc validEmailAddress*(s: string): bool {.noSideEffect,
|
||||
rtl, extern: "nsuValidEmailAddress".} =
|
||||
## returns true if `s` seems to be a valid e-mail address.
|
||||
rtl, extern: "nsuValidEmailAddress".} =
|
||||
## returns true if `s` seems to be a valid e-mail address.
|
||||
## The checking also uses a domain list.
|
||||
const
|
||||
chars = Letters + Digits + {'!','#','$','%','&',
|
||||
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
|
||||
var i = 0
|
||||
if s[i] notin chars or s[i] == '.': return false
|
||||
while s[i] in chars:
|
||||
while s[i] in chars:
|
||||
if s[i] == '.' and s[i+1] == '.': return false
|
||||
inc(i)
|
||||
if s[i] != '@': return false
|
||||
@@ -34,9 +34,9 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
|
||||
if s[j] notin Letters: return false
|
||||
while j >= i and s[j] in Letters: dec(j)
|
||||
inc(i) # skip '@'
|
||||
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
|
||||
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
|
||||
if s[i] != '\0': return false
|
||||
|
||||
|
||||
var x = substr(s, j+1)
|
||||
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
|
||||
case toLower(x)
|
||||
@@ -59,6 +59,6 @@ proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
|
||||
|
||||
when isMainModule:
|
||||
doAssert "wuseldusel@codehome.com".validEmailAddress
|
||||
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ type
|
||||
|
||||
when defined(windows):
|
||||
fHandle: int
|
||||
mapHandle: int
|
||||
mapHandle: int
|
||||
else:
|
||||
handle: cint
|
||||
|
||||
@@ -131,7 +131,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
fail(osLastError(), "error opening file")
|
||||
|
||||
if newFileSize != -1:
|
||||
var
|
||||
var
|
||||
sizeHigh = int32(newFileSize shr 32)
|
||||
sizeLow = int32(newFileSize and 0xffffffff)
|
||||
|
||||
@@ -177,7 +177,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
rollback()
|
||||
if result.handle != 0: discard close(result.handle)
|
||||
raiseOSError(errCode)
|
||||
|
||||
|
||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
||||
|
||||
if newFileSize != -1:
|
||||
@@ -221,7 +221,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
proc close*(f: var MemFile) =
|
||||
## closes the memory mapped file `f`. All changes are written back to the
|
||||
## file system, if `f` was opened with write access.
|
||||
|
||||
|
||||
var error = false
|
||||
var lastErr: OSErrorCode
|
||||
|
||||
@@ -245,7 +245,7 @@ proc close*(f: var MemFile) =
|
||||
f.mapHandle = 0
|
||||
else:
|
||||
f.handle = 0
|
||||
|
||||
|
||||
if error: raiseOSError(lastErr)
|
||||
|
||||
type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records
|
||||
|
||||
@@ -7,7 +7,7 @@ type
|
||||
|
||||
{.deprecated: [TMersenneTwister: MersenneTwister].}
|
||||
|
||||
proc newMersenneTwister*(seed: int): MersenneTwister =
|
||||
proc newMersenneTwister*(seed: int): MersenneTwister =
|
||||
result.index = 0
|
||||
result.mt[0]= uint32(seed)
|
||||
for i in 1..623'u32:
|
||||
|
||||
@@ -11,23 +11,23 @@ type OneVarFunction* = proc (x: float): float
|
||||
|
||||
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
||||
|
||||
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||
tuple[rootx, rooty: float, success: bool]=
|
||||
## Searches `function` for a root between `xmin` and `xmax`
|
||||
## Searches `function` for a root between `xmin` and `xmax`
|
||||
## using brents method. If the function value at `xmin`and `xmax` has the
|
||||
## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
|
||||
## and succes is set to false.
|
||||
## Otherwise there exists at least one root and success is set to true.
|
||||
## This root is searched for at most `maxiter` iterations.
|
||||
## If `tol` tolerance is reached within `maxiter` iterations
|
||||
## If `tol` tolerance is reached within `maxiter` iterations
|
||||
## the root refinement stops and success=true.
|
||||
|
||||
# see http://en.wikipedia.org/wiki/Brent%27s_method
|
||||
var
|
||||
var
|
||||
a=xmin
|
||||
b=xmax
|
||||
c=a
|
||||
d=1.0e308
|
||||
d=1.0e308
|
||||
fa=function(a)
|
||||
fb=function(b)
|
||||
fc=fa
|
||||
@@ -42,19 +42,19 @@ proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||
return (a,fa,false)
|
||||
else:
|
||||
return (b,fb,false)
|
||||
|
||||
|
||||
if abs(fa)<abs(fb):
|
||||
swap(fa,fb)
|
||||
swap(a,b)
|
||||
|
||||
|
||||
while fb!=0.0 and abs(a-b)>tol:
|
||||
if fa!=fc and fb!=fc: # inverse quadratic interpolation
|
||||
s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
|
||||
else: #secant rule
|
||||
s = b - fb * (b - a) / (fb - fa)
|
||||
tmp2 = (3.0 * a + b) / 4.0
|
||||
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
|
||||
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
|
||||
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
|
||||
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
|
||||
(not mflag and abs(s - b) >= abs(c - d) / 2.0):
|
||||
s=(a+b)/2.0
|
||||
mflag=true
|
||||
@@ -80,5 +80,5 @@ proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||
inc i
|
||||
if i>maxiter:
|
||||
break
|
||||
|
||||
|
||||
return (b,fb,true)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
## Nim OID support. An OID is a global ID that consists of a timestamp,
|
||||
## a unique counter and a random value. This combination should suffice to
|
||||
## a unique counter and a random value. This combination should suffice to
|
||||
## produce a globally distributed unique ID. This implementation was extracted
|
||||
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
||||
##
|
||||
@@ -19,13 +19,13 @@ import times, endians
|
||||
|
||||
type
|
||||
Oid* = object ## an OID
|
||||
time: int32 ##
|
||||
fuzz: int32 ##
|
||||
count: int32 ##
|
||||
time: int32 ##
|
||||
fuzz: int32 ##
|
||||
count: int32 ##
|
||||
|
||||
{.deprecated: [Toid: Oid].}
|
||||
|
||||
proc hexbyte*(hex: char): int =
|
||||
proc hexbyte*(hex: char): int =
|
||||
case hex
|
||||
of '0'..'9': result = (ord(hex) - ord('0'))
|
||||
of 'a'..'f': result = (ord(hex) - ord('a') + 10)
|
||||
@@ -40,7 +40,7 @@ proc parseOid*(str: cstring): Oid =
|
||||
bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
|
||||
inc(i)
|
||||
|
||||
proc oidToString*(oid: Oid, str: cstring) =
|
||||
proc oidToString*(oid: Oid, str: cstring) =
|
||||
const hex = "0123456789abcdef"
|
||||
# work around a compiler bug:
|
||||
var str = str
|
||||
@@ -59,7 +59,7 @@ proc `$`*(oid: Oid): string =
|
||||
oidToString(oid, result)
|
||||
|
||||
var
|
||||
incr: int
|
||||
incr: int
|
||||
fuzz: int32
|
||||
|
||||
proc genOid*(): Oid =
|
||||
@@ -69,10 +69,10 @@ proc genOid*(): Oid =
|
||||
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
|
||||
|
||||
var t = gettime(nil)
|
||||
|
||||
|
||||
var i = int32(incr)
|
||||
atomicInc(incr)
|
||||
|
||||
|
||||
if fuzz == 0:
|
||||
# racy, but fine semantically:
|
||||
srand(t)
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The ``parsecfg`` module implements a high performance configuration file
|
||||
## parser. The configuration file's syntax is similar to the Windows ``.ini``
|
||||
## format, but much more powerful, as it is not a line based parser. String
|
||||
## literals, raw string literals and triple quoted string literals are supported
|
||||
## The ``parsecfg`` module implements a high performance configuration file
|
||||
## parser. The configuration file's syntax is similar to the Windows ``.ini``
|
||||
## format, but much more powerful, as it is not a line based parser. String
|
||||
## literals, raw string literals and triple quoted string literals are supported
|
||||
## as in the Nim programming language.
|
||||
|
||||
## This is an example of how a configuration file may look like:
|
||||
##
|
||||
## .. include:: doc/mytest.cfg
|
||||
## :literal:
|
||||
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
|
||||
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
|
||||
## configuration file parser:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
@@ -36,14 +36,14 @@ type
|
||||
cfgKeyValuePair, ## a ``key=value`` pair has been detected
|
||||
cfgOption, ## a ``--key=value`` command line option
|
||||
cfgError ## an error occurred during parsing
|
||||
|
||||
|
||||
CfgEvent* = object of RootObj ## describes a parsing event
|
||||
case kind*: CfgEventKind ## the kind of the event
|
||||
of cfgEof: nil
|
||||
of cfgSectionStart:
|
||||
section*: string ## `section` contains the name of the
|
||||
of cfgSectionStart:
|
||||
section*: string ## `section` contains the name of the
|
||||
## parsed section start (syntax: ``[section]``)
|
||||
of cfgKeyValuePair, cfgOption:
|
||||
of cfgKeyValuePair, cfgOption:
|
||||
key*, value*: string ## contains the (key, value) pair if an option
|
||||
## of the form ``--key: value`` or an ordinary
|
||||
## ``key= value`` pair has been parsed.
|
||||
@@ -52,14 +52,14 @@ type
|
||||
of cfgError: ## the parser encountered an error: `msg`
|
||||
msg*: string ## contains the error message. No exceptions
|
||||
## are thrown if a parse error occurs.
|
||||
|
||||
TokKind = enum
|
||||
tkInvalid, tkEof,
|
||||
|
||||
TokKind = enum
|
||||
tkInvalid, tkEof,
|
||||
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
|
||||
Token = object # a token
|
||||
kind: TokKind # the type of the token
|
||||
literal: string # the parsed (string) literal
|
||||
|
||||
|
||||
CfgParser* = object of BaseLexer ## the parser object.
|
||||
tok: Token
|
||||
filename: string
|
||||
@@ -69,12 +69,12 @@ type
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'}
|
||||
|
||||
const
|
||||
SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'}
|
||||
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
|
||||
|
||||
proc open*(c: var CfgParser, input: Stream, filename: string,
|
||||
proc open*(c: var CfgParser, input: Stream, filename: string,
|
||||
lineOffset = 0) {.rtl, extern: "npc$1".} =
|
||||
## initializes the parser with an input stream. `Filename` is only used
|
||||
## for nice error messages. `lineOffset` can be used to influence the line
|
||||
@@ -85,7 +85,7 @@ proc open*(c: var CfgParser, input: Stream, filename: string,
|
||||
c.tok.literal = ""
|
||||
inc(c.lineNumber, lineOffset)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
|
||||
proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} =
|
||||
## closes the parser `c` and its associated input stream.
|
||||
lexbase.close(c)
|
||||
@@ -102,260 +102,260 @@ proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} =
|
||||
## get the filename of the file that the parser processes.
|
||||
result = c.filename
|
||||
|
||||
proc handleHexChar(c: var CfgParser, xi: var int) =
|
||||
proc handleHexChar(c: var CfgParser, xi: var int) =
|
||||
case c.buf[c.bufpos]
|
||||
of '0'..'9':
|
||||
of '0'..'9':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
of 'a'..'f':
|
||||
of 'a'..'f':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
|
||||
inc(c.bufpos)
|
||||
of 'A'..'F':
|
||||
of 'A'..'F':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else:
|
||||
else:
|
||||
discard
|
||||
|
||||
proc handleDecChars(c: var CfgParser, xi: var int) =
|
||||
while c.buf[c.bufpos] in {'0'..'9'}:
|
||||
proc handleDecChars(c: var CfgParser, xi: var int) =
|
||||
while c.buf[c.bufpos] in {'0'..'9'}:
|
||||
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
|
||||
proc getEscapedChar(c: var CfgParser, tok: var Token) =
|
||||
proc getEscapedChar(c: var CfgParser, tok: var Token) =
|
||||
inc(c.bufpos) # skip '\'
|
||||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
of 'n', 'N':
|
||||
add(tok.literal, "\n")
|
||||
inc(c.bufpos)
|
||||
of 'r', 'R', 'c', 'C':
|
||||
of 'r', 'R', 'c', 'C':
|
||||
add(tok.literal, '\c')
|
||||
inc(c.bufpos)
|
||||
of 'l', 'L':
|
||||
of 'l', 'L':
|
||||
add(tok.literal, '\L')
|
||||
inc(c.bufpos)
|
||||
of 'f', 'F':
|
||||
of 'f', 'F':
|
||||
add(tok.literal, '\f')
|
||||
inc(c.bufpos)
|
||||
of 'e', 'E':
|
||||
of 'e', 'E':
|
||||
add(tok.literal, '\e')
|
||||
inc(c.bufpos)
|
||||
of 'a', 'A':
|
||||
of 'a', 'A':
|
||||
add(tok.literal, '\a')
|
||||
inc(c.bufpos)
|
||||
of 'b', 'B':
|
||||
of 'b', 'B':
|
||||
add(tok.literal, '\b')
|
||||
inc(c.bufpos)
|
||||
of 'v', 'V':
|
||||
of 'v', 'V':
|
||||
add(tok.literal, '\v')
|
||||
inc(c.bufpos)
|
||||
of 't', 'T':
|
||||
of 't', 'T':
|
||||
add(tok.literal, '\t')
|
||||
inc(c.bufpos)
|
||||
of '\'', '"':
|
||||
of '\'', '"':
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
inc(c.bufpos)
|
||||
of '\\':
|
||||
of '\\':
|
||||
add(tok.literal, '\\')
|
||||
inc(c.bufpos)
|
||||
of 'x', 'X':
|
||||
of 'x', 'X':
|
||||
inc(c.bufpos)
|
||||
var xi = 0
|
||||
handleHexChar(c, xi)
|
||||
handleHexChar(c, xi)
|
||||
add(tok.literal, chr(xi))
|
||||
of '0'..'9':
|
||||
of '0'..'9':
|
||||
var xi = 0
|
||||
handleDecChars(c, xi)
|
||||
if (xi <= 255): add(tok.literal, chr(xi))
|
||||
else: tok.kind = tkInvalid
|
||||
else: tok.kind = tkInvalid
|
||||
|
||||
proc handleCRLF(c: var CfgParser, pos: int): int =
|
||||
|
||||
proc handleCRLF(c: var CfgParser, pos: int): int =
|
||||
case c.buf[pos]
|
||||
of '\c': result = lexbase.handleCR(c, pos)
|
||||
of '\L': result = lexbase.handleLF(c, pos)
|
||||
else: result = pos
|
||||
|
||||
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
|
||||
|
||||
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
|
||||
var pos = c.bufpos + 1 # skip "
|
||||
var buf = c.buf # put `buf` in a register
|
||||
tok.kind = tkSymbol
|
||||
if (buf[pos] == '"') and (buf[pos + 1] == '"'):
|
||||
if (buf[pos] == '"') and (buf[pos + 1] == '"'):
|
||||
# long string literal:
|
||||
inc(pos, 2) # skip ""
|
||||
# skip leading newline:
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
while true:
|
||||
while true:
|
||||
case buf[pos]
|
||||
of '"':
|
||||
if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
|
||||
of '"':
|
||||
if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
|
||||
add(tok.literal, '"')
|
||||
inc(pos)
|
||||
of '\c', '\L':
|
||||
of '\c', '\L':
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
add(tok.literal, "\n")
|
||||
of lexbase.EndOfFile:
|
||||
of lexbase.EndOfFile:
|
||||
tok.kind = tkInvalid
|
||||
break
|
||||
else:
|
||||
break
|
||||
else:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
c.bufpos = pos + 3 # skip the three """
|
||||
else:
|
||||
else:
|
||||
# ordinary string literal
|
||||
while true:
|
||||
while true:
|
||||
var ch = buf[pos]
|
||||
if ch == '"':
|
||||
if ch == '"':
|
||||
inc(pos) # skip '"'
|
||||
break
|
||||
if ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
break
|
||||
if ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
tok.kind = tkInvalid
|
||||
break
|
||||
if (ch == '\\') and not rawMode:
|
||||
break
|
||||
if (ch == '\\') and not rawMode:
|
||||
c.bufpos = pos
|
||||
getEscapedChar(c, tok)
|
||||
pos = c.bufpos
|
||||
else:
|
||||
else:
|
||||
add(tok.literal, ch)
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getSymbol(c: var CfgParser, tok: var Token) =
|
||||
proc getSymbol(c: var CfgParser, tok: var Token) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
while true:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
if not (buf[pos] in SymChars): break
|
||||
if not (buf[pos] in SymChars): break
|
||||
c.bufpos = pos
|
||||
tok.kind = tkSymbol
|
||||
|
||||
proc skip(c: var CfgParser) =
|
||||
proc skip(c: var CfgParser) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
while true:
|
||||
case buf[pos]
|
||||
of ' ', '\t':
|
||||
of ' ', '\t':
|
||||
inc(pos)
|
||||
of '#', ';':
|
||||
of '#', ';':
|
||||
while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
|
||||
of '\c', '\L':
|
||||
of '\c', '\L':
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
else:
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) =
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) =
|
||||
tok.kind = tkInvalid
|
||||
setLen(tok.literal, 0)
|
||||
skip(c)
|
||||
case c.buf[c.bufpos]
|
||||
of '=':
|
||||
of '=':
|
||||
tok.kind = tkEquals
|
||||
inc(c.bufpos)
|
||||
tok.literal = "="
|
||||
of '-':
|
||||
of '-':
|
||||
inc(c.bufpos)
|
||||
if c.buf[c.bufpos] == '-': inc(c.bufpos)
|
||||
tok.kind = tkDashDash
|
||||
tok.literal = "--"
|
||||
of ':':
|
||||
of ':':
|
||||
tok.kind = tkColon
|
||||
inc(c.bufpos)
|
||||
tok.literal = ":"
|
||||
of 'r', 'R':
|
||||
if c.buf[c.bufpos + 1] == '\"':
|
||||
of 'r', 'R':
|
||||
if c.buf[c.bufpos + 1] == '\"':
|
||||
inc(c.bufpos)
|
||||
getString(c, tok, true)
|
||||
else:
|
||||
else:
|
||||
getSymbol(c, tok)
|
||||
of '[':
|
||||
of '[':
|
||||
tok.kind = tkBracketLe
|
||||
inc(c.bufpos)
|
||||
tok.literal = "]"
|
||||
of ']':
|
||||
of ']':
|
||||
tok.kind = tkBracketRi
|
||||
inc(c.bufpos)
|
||||
tok.literal = "]"
|
||||
of '"':
|
||||
of '"':
|
||||
getString(c, tok, false)
|
||||
of lexbase.EndOfFile:
|
||||
of lexbase.EndOfFile:
|
||||
tok.kind = tkEof
|
||||
tok.literal = "[EOF]"
|
||||
else: getSymbol(c, tok)
|
||||
|
||||
|
||||
proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## returns a properly formated error message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Error: $4",
|
||||
result = `%`("$1($2, $3) Error: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
|
||||
|
||||
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## returns a properly formated warning message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Warning: $4",
|
||||
result = `%`("$1($2, $3) Warning: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
|
||||
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
|
||||
## returns a properly formated warning message containing that
|
||||
## an entry is ignored.
|
||||
case e.kind
|
||||
case e.kind
|
||||
of cfgSectionStart: result = c.warningStr("section ignored: " & e.section)
|
||||
of cfgKeyValuePair: result = c.warningStr("key ignored: " & e.key)
|
||||
of cfgOption:
|
||||
of cfgOption:
|
||||
result = c.warningStr("command ignored: " & e.key & ": " & e.value)
|
||||
of cfgError: result = e.msg
|
||||
of cfgEof: result = ""
|
||||
|
||||
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
|
||||
if c.tok.kind == tkSymbol:
|
||||
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.kind = kind
|
||||
result.key = c.tok.literal
|
||||
result.value = ""
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind in {tkEquals, tkColon}:
|
||||
if c.tok.kind in {tkEquals, tkColon}:
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind == tkSymbol:
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.value = c.tok.literal
|
||||
else:
|
||||
else:
|
||||
reset result
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
else:
|
||||
else:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
|
||||
## retrieves the first/next event. This controls the parser.
|
||||
case c.tok.kind
|
||||
of tkEof:
|
||||
case c.tok.kind
|
||||
of tkEof:
|
||||
result.kind = cfgEof
|
||||
of tkDashDash:
|
||||
of tkDashDash:
|
||||
rawGetTok(c, c.tok)
|
||||
result = getKeyValPair(c, cfgOption)
|
||||
of tkSymbol:
|
||||
of tkSymbol:
|
||||
result = getKeyValPair(c, cfgKeyValuePair)
|
||||
of tkBracketLe:
|
||||
of tkBracketLe:
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind == tkSymbol:
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.kind = cfgSectionStart
|
||||
result.section = c.tok.literal
|
||||
else:
|
||||
else:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
if c.tok.kind == tkBracketRi:
|
||||
if c.tok.kind == tkBracketRi:
|
||||
rawGetTok(c, c.tok)
|
||||
else:
|
||||
reset(result)
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal)
|
||||
of tkInvalid, tkEquals, tkColon, tkBracketRi:
|
||||
of tkInvalid, tkEquals, tkColon, tkBracketRi:
|
||||
result.kind = cfgError
|
||||
result.msg = errorStr(c, "invalid token: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
## This module implements a simple high performance `CSV`:idx:
|
||||
## (`comma separated value`:idx:) parser.
|
||||
## (`comma separated value`:idx:) parser.
|
||||
##
|
||||
## Example: How to use the parser
|
||||
## ==============================
|
||||
@@ -43,7 +43,7 @@ type
|
||||
|
||||
{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].}
|
||||
|
||||
proc raiseEInvalidCsv(filename: string, line, col: int,
|
||||
proc raiseEInvalidCsv(filename: string, line, col: int,
|
||||
msg: string) {.noreturn.} =
|
||||
var e: ref CsvError
|
||||
new(e)
|
||||
@@ -60,13 +60,13 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
|
||||
## for nice error messages. The parser's behaviour can be controlled by
|
||||
## the diverse optional parameters:
|
||||
## - `separator`: character used to separate fields
|
||||
## - `quote`: Used to quote fields containing special characters like
|
||||
## - `quote`: Used to quote fields containing special characters like
|
||||
## `separator`, `quote` or new-line characters. '\0' disables the parsing
|
||||
## of quotes.
|
||||
## - `escape`: removes any special meaning from the following character;
|
||||
## - `escape`: removes any special meaning from the following character;
|
||||
## '\0' disables escaping; if escaping is disabled and `quote` is not '\0',
|
||||
## two `quote` characters are parsed one literal `quote` character.
|
||||
## - `skipInitialSpace`: If true, whitespace immediately following the
|
||||
## - `skipInitialSpace`: If true, whitespace immediately following the
|
||||
## `separator` is ignored.
|
||||
lexbase.open(my, input)
|
||||
my.filename = filename
|
||||
@@ -77,13 +77,13 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
|
||||
my.row = @[]
|
||||
my.currRow = 0
|
||||
|
||||
proc parseField(my: var CsvParser, a: var string) =
|
||||
proc parseField(my: var CsvParser, a: var string) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
if my.skipWhite:
|
||||
while buf[pos] in {' ', '\t'}: inc(pos)
|
||||
setLen(a, 0) # reuse memory
|
||||
if buf[pos] == my.quote and my.quote != '\0':
|
||||
if buf[pos] == my.quote and my.quote != '\0':
|
||||
inc(pos)
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
@@ -91,7 +91,7 @@ proc parseField(my: var CsvParser, a: var string) =
|
||||
my.bufpos = pos # can continue after exception?
|
||||
error(my, pos, my.quote & " expected")
|
||||
break
|
||||
elif c == my.quote:
|
||||
elif c == my.quote:
|
||||
if my.esc == '\0' and buf[pos+1] == my.quote:
|
||||
add(a, my.quote)
|
||||
inc(pos, 2)
|
||||
@@ -103,11 +103,11 @@ proc parseField(my: var CsvParser, a: var string) =
|
||||
inc(pos, 2)
|
||||
else:
|
||||
case c
|
||||
of '\c':
|
||||
of '\c':
|
||||
pos = handleCR(my, pos)
|
||||
buf = my.buf
|
||||
add(a, "\n")
|
||||
of '\l':
|
||||
of '\l':
|
||||
pos = handleLF(my, pos)
|
||||
buf = my.buf
|
||||
add(a, "\n")
|
||||
@@ -123,11 +123,11 @@ proc parseField(my: var CsvParser, a: var string) =
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc processedRows*(my: var CsvParser): int =
|
||||
proc processedRows*(my: var CsvParser): int =
|
||||
## returns number of the processed rows
|
||||
return my.currRow
|
||||
|
||||
proc readRow*(my: var CsvParser, columns = 0): bool =
|
||||
proc readRow*(my: var CsvParser, columns = 0): bool =
|
||||
## reads the next row; if `columns` > 0, it expects the row to have
|
||||
## exactly this many columns. Returns false if the end of the file
|
||||
## has been encountered else true.
|
||||
@@ -140,13 +140,13 @@ proc readRow*(my: var CsvParser, columns = 0): bool =
|
||||
my.row[col] = ""
|
||||
parseField(my, my.row[col])
|
||||
inc(col)
|
||||
if my.buf[my.bufpos] == my.sep:
|
||||
if my.buf[my.bufpos] == my.sep:
|
||||
inc(my.bufpos)
|
||||
else:
|
||||
case my.buf[my.bufpos]
|
||||
of '\c', '\l':
|
||||
of '\c', '\l':
|
||||
# skip empty lines:
|
||||
while true:
|
||||
while true:
|
||||
case my.buf[my.bufpos]
|
||||
of '\c': my.bufpos = handleCR(my, my.bufpos)
|
||||
of '\l': my.bufpos = handleLF(my, my.bufpos)
|
||||
@@ -154,15 +154,15 @@ proc readRow*(my: var CsvParser, columns = 0): bool =
|
||||
of '\0': discard
|
||||
else: error(my, my.bufpos, my.sep & " expected")
|
||||
break
|
||||
|
||||
|
||||
setLen(my.row, col)
|
||||
result = col > 0
|
||||
if result and col != columns and columns > 0:
|
||||
error(my, oldpos+1, $columns & " columns expected, but found " &
|
||||
if result and col != columns and columns > 0:
|
||||
error(my, oldpos+1, $columns & " columns expected, but found " &
|
||||
$col & " columns")
|
||||
inc(my.currRow)
|
||||
|
||||
proc close*(my: var CsvParser) {.inline.} =
|
||||
|
||||
proc close*(my: var CsvParser) {.inline.} =
|
||||
## closes the parser `my` and its associated input stream.
|
||||
lexbase.close(my)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The ``parsesql`` module implements a high performance SQL file
|
||||
## The ``parsesql`` module implements a high performance SQL file
|
||||
## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
|
||||
|
||||
import
|
||||
@@ -37,11 +37,11 @@ type
|
||||
tkBracketLe, ## '['
|
||||
tkBracketRi, ## ']'
|
||||
tkDot ## '.'
|
||||
|
||||
|
||||
Token = object # a token
|
||||
kind: TokKind # the type of the token
|
||||
literal: string # the parsed (string) literal
|
||||
|
||||
|
||||
SqlLexer* = object of BaseLexer ## the parser object.
|
||||
filename: string
|
||||
|
||||
@@ -55,82 +55,82 @@ const
|
||||
";", ":", ",", "(", ")", "[", "]", "."
|
||||
]
|
||||
|
||||
proc open(L: var SqlLexer, input: Stream, filename: string) =
|
||||
proc open(L: var SqlLexer, input: Stream, filename: string) =
|
||||
lexbase.open(L, input)
|
||||
L.filename = filename
|
||||
|
||||
proc close(L: var SqlLexer) =
|
||||
|
||||
proc close(L: var SqlLexer) =
|
||||
lexbase.close(L)
|
||||
|
||||
proc getColumn(L: SqlLexer): int =
|
||||
proc getColumn(L: SqlLexer): int =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(L, L.bufpos)
|
||||
|
||||
proc getLine(L: SqlLexer): int =
|
||||
proc getLine(L: SqlLexer): int =
|
||||
result = L.lineNumber
|
||||
|
||||
proc handleHexChar(c: var SqlLexer, xi: var int) =
|
||||
proc handleHexChar(c: var SqlLexer, xi: var int) =
|
||||
case c.buf[c.bufpos]
|
||||
of '0'..'9':
|
||||
of '0'..'9':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
of 'a'..'f':
|
||||
of 'a'..'f':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
|
||||
inc(c.bufpos)
|
||||
of 'A'..'F':
|
||||
of 'A'..'F':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else:
|
||||
else:
|
||||
discard
|
||||
|
||||
proc handleOctChar(c: var SqlLexer, xi: var int) =
|
||||
proc handleOctChar(c: var SqlLexer, xi: var int) =
|
||||
if c.buf[c.bufpos] in {'0'..'7'}:
|
||||
xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
|
||||
proc getEscapedChar(c: var SqlLexer, tok: var Token) =
|
||||
proc getEscapedChar(c: var SqlLexer, tok: var Token) =
|
||||
inc(c.bufpos)
|
||||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
of 'n', 'N':
|
||||
add(tok.literal, '\L')
|
||||
inc(c.bufpos)
|
||||
of 'r', 'R', 'c', 'C':
|
||||
of 'r', 'R', 'c', 'C':
|
||||
add(tok.literal, '\c')
|
||||
inc(c.bufpos)
|
||||
of 'l', 'L':
|
||||
of 'l', 'L':
|
||||
add(tok.literal, '\L')
|
||||
inc(c.bufpos)
|
||||
of 'f', 'F':
|
||||
of 'f', 'F':
|
||||
add(tok.literal, '\f')
|
||||
inc(c.bufpos)
|
||||
of 'e', 'E':
|
||||
of 'e', 'E':
|
||||
add(tok.literal, '\e')
|
||||
inc(c.bufpos)
|
||||
of 'a', 'A':
|
||||
of 'a', 'A':
|
||||
add(tok.literal, '\a')
|
||||
inc(c.bufpos)
|
||||
of 'b', 'B':
|
||||
of 'b', 'B':
|
||||
add(tok.literal, '\b')
|
||||
inc(c.bufpos)
|
||||
of 'v', 'V':
|
||||
of 'v', 'V':
|
||||
add(tok.literal, '\v')
|
||||
inc(c.bufpos)
|
||||
of 't', 'T':
|
||||
of 't', 'T':
|
||||
add(tok.literal, '\t')
|
||||
inc(c.bufpos)
|
||||
of '\'', '\"':
|
||||
of '\'', '\"':
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
inc(c.bufpos)
|
||||
of '\\':
|
||||
of '\\':
|
||||
add(tok.literal, '\\')
|
||||
inc(c.bufpos)
|
||||
of 'x', 'X':
|
||||
of 'x', 'X':
|
||||
inc(c.bufpos)
|
||||
var xi = 0
|
||||
handleHexChar(c, xi)
|
||||
handleHexChar(c, xi)
|
||||
add(tok.literal, chr(xi))
|
||||
of '0'..'7':
|
||||
of '0'..'7':
|
||||
var xi = 0
|
||||
handleOctChar(c, xi)
|
||||
handleOctChar(c, xi)
|
||||
@@ -138,20 +138,20 @@ proc getEscapedChar(c: var SqlLexer, tok: var Token) =
|
||||
if (xi <= 255): add(tok.literal, chr(xi))
|
||||
else: tok.kind = tkInvalid
|
||||
else: tok.kind = tkInvalid
|
||||
|
||||
proc handleCRLF(c: var SqlLexer, pos: int): int =
|
||||
|
||||
proc handleCRLF(c: var SqlLexer, pos: int): int =
|
||||
case c.buf[pos]
|
||||
of '\c': result = lexbase.handleCR(c, pos)
|
||||
of '\L': result = lexbase.handleLF(c, pos)
|
||||
else: result = pos
|
||||
|
||||
proc skip(c: var SqlLexer) =
|
||||
proc skip(c: var SqlLexer) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
var nested = 0
|
||||
while true:
|
||||
while true:
|
||||
case buf[pos]
|
||||
of ' ', '\t':
|
||||
of ' ', '\t':
|
||||
inc(pos)
|
||||
of '-':
|
||||
if buf[pos+1] == '-':
|
||||
@@ -164,7 +164,7 @@ proc skip(c: var SqlLexer) =
|
||||
while true:
|
||||
case buf[pos]
|
||||
of '\0': break
|
||||
of '\c', '\L':
|
||||
of '\c', '\L':
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
of '*':
|
||||
@@ -182,20 +182,20 @@ proc skip(c: var SqlLexer) =
|
||||
inc(pos)
|
||||
else: inc(pos)
|
||||
else: break
|
||||
of '\c', '\L':
|
||||
of '\c', '\L':
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
else:
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
|
||||
|
||||
proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
|
||||
var pos = c.bufpos + 1
|
||||
var buf = c.buf
|
||||
tok.kind = kind
|
||||
block parseLoop:
|
||||
while true:
|
||||
while true:
|
||||
while true:
|
||||
var ch = buf[pos]
|
||||
if ch == '\'':
|
||||
if buf[pos+1] == '\'':
|
||||
@@ -203,15 +203,15 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
|
||||
add(tok.literal, '\'')
|
||||
else:
|
||||
inc(pos)
|
||||
break
|
||||
elif ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
break
|
||||
elif ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
tok.kind = tkInvalid
|
||||
break parseLoop
|
||||
elif (ch == '\\') and kind == tkEscapeConstant:
|
||||
elif (ch == '\\') and kind == tkEscapeConstant:
|
||||
c.bufpos = pos
|
||||
getEscapedChar(c, tok)
|
||||
pos = c.bufpos
|
||||
else:
|
||||
else:
|
||||
add(tok.literal, ch)
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
@@ -227,7 +227,7 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
|
||||
else: break parseLoop
|
||||
c.bufpos = pos
|
||||
|
||||
proc getDollarString(c: var SqlLexer, tok: var Token) =
|
||||
proc getDollarString(c: var SqlLexer, tok: var Token) =
|
||||
var pos = c.bufpos + 1
|
||||
var buf = c.buf
|
||||
tok.kind = tkDollarQuotedConstant
|
||||
@@ -241,7 +241,7 @@ proc getDollarString(c: var SqlLexer, tok: var Token) =
|
||||
return
|
||||
while true:
|
||||
case buf[pos]
|
||||
of '\c', '\L':
|
||||
of '\c', '\L':
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
add(tok.literal, "\L")
|
||||
@@ -263,10 +263,10 @@ proc getDollarString(c: var SqlLexer, tok: var Token) =
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getSymbol(c: var SqlLexer, tok: var Token) =
|
||||
proc getSymbol(c: var SqlLexer, tok: var Token) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
while true:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}:
|
||||
@@ -274,7 +274,7 @@ proc getSymbol(c: var SqlLexer, tok: var Token) =
|
||||
c.bufpos = pos
|
||||
tok.kind = tkIdentifier
|
||||
|
||||
proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
|
||||
proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
|
||||
var pos = c.bufpos + 1
|
||||
var buf = c.buf
|
||||
tok.kind = tkQuotedIdentifier
|
||||
@@ -287,7 +287,7 @@ proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
|
||||
else:
|
||||
inc(pos)
|
||||
break
|
||||
elif ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
elif ch in {'\c', '\L', lexbase.EndOfFile}:
|
||||
tok.kind = tkInvalid
|
||||
break
|
||||
else:
|
||||
@@ -300,15 +300,15 @@ proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) =
|
||||
var buf = c.buf
|
||||
block parseLoop:
|
||||
while true:
|
||||
while true:
|
||||
while true:
|
||||
var ch = buf[pos]
|
||||
if ch in validChars:
|
||||
add(tok.literal, ch)
|
||||
inc(pos)
|
||||
inc(pos)
|
||||
elif ch == '\'':
|
||||
inc(pos)
|
||||
break
|
||||
else:
|
||||
else:
|
||||
tok.kind = tkInvalid
|
||||
break parseLoop
|
||||
c.bufpos = pos
|
||||
@@ -353,7 +353,7 @@ proc getNumeric(c: var SqlLexer, tok: var Token) =
|
||||
inc(pos)
|
||||
else:
|
||||
tok.kind = tkInvalid
|
||||
c.bufpos = pos
|
||||
c.bufpos = pos
|
||||
|
||||
proc getOperator(c: var SqlLexer, tok: var Token) =
|
||||
const operators = {'+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%',
|
||||
@@ -381,12 +381,12 @@ proc getOperator(c: var SqlLexer, tok: var Token) =
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
tok.kind = tkInvalid
|
||||
setLen(tok.literal, 0)
|
||||
skip(c)
|
||||
case c.buf[c.bufpos]
|
||||
of ';':
|
||||
of ';':
|
||||
tok.kind = tkSemicolon
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, ';')
|
||||
@@ -394,15 +394,15 @@ proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
tok.kind = tkComma
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, ',')
|
||||
of ':':
|
||||
of ':':
|
||||
tok.kind = tkColon
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, ':')
|
||||
of 'e', 'E':
|
||||
if c.buf[c.bufpos + 1] == '\'':
|
||||
of 'e', 'E':
|
||||
if c.buf[c.bufpos + 1] == '\'':
|
||||
inc(c.bufpos)
|
||||
getString(c, tok, tkEscapeConstant)
|
||||
else:
|
||||
else:
|
||||
getSymbol(c, tok)
|
||||
of 'b', 'B':
|
||||
if c.buf[c.bufpos + 1] == '\'':
|
||||
@@ -417,11 +417,11 @@ proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
else:
|
||||
getSymbol(c, tok)
|
||||
of '$': getDollarString(c, tok)
|
||||
of '[':
|
||||
of '[':
|
||||
tok.kind = tkBracketLe
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, '[')
|
||||
of ']':
|
||||
of ']':
|
||||
tok.kind = tkBracketRi
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, ']')
|
||||
@@ -433,7 +433,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
tok.kind = tkParRi
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, ')')
|
||||
of '.':
|
||||
of '.':
|
||||
if c.buf[c.bufpos + 1] in Digits:
|
||||
getNumeric(c, tok)
|
||||
else:
|
||||
@@ -443,7 +443,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
of '0'..'9': getNumeric(c, tok)
|
||||
of '\'': getString(c, tok, tkStringConstant)
|
||||
of '"': getQuotedIdentifier(c, tok)
|
||||
of lexbase.EndOfFile:
|
||||
of lexbase.EndOfFile:
|
||||
tok.kind = tkEof
|
||||
tok.literal = "[EOF]"
|
||||
of 'a', 'c', 'd', 'f'..'w', 'y', 'z', 'A', 'C', 'D', 'F'..'W', 'Y', 'Z', '_',
|
||||
@@ -455,8 +455,8 @@ proc getTok(c: var SqlLexer, tok: var Token) =
|
||||
else:
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
inc(c.bufpos)
|
||||
|
||||
proc errorStr(L: SqlLexer, msg: string): string =
|
||||
|
||||
proc errorStr(L: SqlLexer, msg: string): string =
|
||||
result = "$1($2, $3) Error: $4" % [L.filename, $getLine(L), $getColumn(L), msg]
|
||||
|
||||
|
||||
@@ -496,7 +496,7 @@ type
|
||||
nkPrimaryKey,
|
||||
nkForeignKey,
|
||||
nkNotNull,
|
||||
|
||||
|
||||
nkStmtList,
|
||||
nkDot,
|
||||
nkDotDot,
|
||||
@@ -530,13 +530,13 @@ type
|
||||
nkValueList,
|
||||
nkWhere,
|
||||
nkCreateTable,
|
||||
nkCreateTableIfNotExists,
|
||||
nkCreateTableIfNotExists,
|
||||
nkCreateType,
|
||||
nkCreateTypeIfNotExists,
|
||||
nkCreateIndex,
|
||||
nkCreateIndexIfNotExists,
|
||||
nkEnumDef
|
||||
|
||||
|
||||
type
|
||||
SqlParseError* = object of ValueError ## Invalid SQL encountered
|
||||
SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
|
||||
@@ -563,11 +563,11 @@ proc newNode(k: SqlNodeKind, s: string): SqlNode =
|
||||
new(result)
|
||||
result.kind = k
|
||||
result.strVal = s
|
||||
|
||||
|
||||
proc len*(n: SqlNode): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = n.sons.len
|
||||
|
||||
|
||||
proc add*(father, n: SqlNode) =
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
add(father.sons, n)
|
||||
@@ -641,11 +641,11 @@ proc parseDataType(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc getPrecedence(p: SqlParser): int =
|
||||
proc getPrecedence(p: SqlParser): int =
|
||||
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
|
||||
result = 6
|
||||
elif isOpr(p, "+") or isOpr(p, "-"):
|
||||
result = 5
|
||||
result = 5
|
||||
elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or
|
||||
isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or
|
||||
isKeyw(p, "like"):
|
||||
@@ -664,7 +664,7 @@ proc parseExpr(p: var SqlParser): SqlNode
|
||||
|
||||
proc identOrLiteral(p: var SqlParser): SqlNode =
|
||||
case p.tok.kind
|
||||
of tkIdentifier, tkQuotedIdentifier:
|
||||
of tkIdentifier, tkQuotedIdentifier:
|
||||
result = newNode(nkIdent, p.tok.literal)
|
||||
getTok(p)
|
||||
of tkStringConstant, tkEscapeConstant, tkDollarQuotedConstant:
|
||||
@@ -686,21 +686,21 @@ proc identOrLiteral(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = parseExpr(p)
|
||||
eat(p, tkParRi)
|
||||
else:
|
||||
else:
|
||||
sqlError(p, "expression expected")
|
||||
getTok(p) # we must consume a token here to prevend endless loops!
|
||||
|
||||
proc primary(p: var SqlParser): SqlNode =
|
||||
if p.tok.kind == tkOperator or isKeyw(p, "not"):
|
||||
proc primary(p: var SqlParser): SqlNode =
|
||||
if p.tok.kind == tkOperator or isKeyw(p, "not"):
|
||||
result = newNode(nkPrefix)
|
||||
result.add(newNode(nkIdent, p.tok.literal))
|
||||
getTok(p)
|
||||
result.add(primary(p))
|
||||
return
|
||||
result = identOrLiteral(p)
|
||||
while true:
|
||||
while true:
|
||||
case p.tok.kind
|
||||
of tkParLe:
|
||||
of tkParLe:
|
||||
var a = result
|
||||
result = newNode(nkCall)
|
||||
result.add(a)
|
||||
@@ -710,7 +710,7 @@ proc primary(p: var SqlParser): SqlNode =
|
||||
if p.tok.kind == tkComma: getTok(p)
|
||||
else: break
|
||||
eat(p, tkParRi)
|
||||
of tkDot:
|
||||
of tkDot:
|
||||
getTok(p)
|
||||
var a = result
|
||||
if p.tok.kind == tkDot:
|
||||
@@ -727,14 +727,14 @@ proc primary(p: var SqlParser): SqlNode =
|
||||
sqlError(p, "identifier expected")
|
||||
getTok(p)
|
||||
else: break
|
||||
|
||||
proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
|
||||
|
||||
proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
|
||||
var
|
||||
v2, node, opNode: SqlNode
|
||||
v = primary(p) # expand while operators have priorities higher than 'limit'
|
||||
var opPred = getPrecedence(p)
|
||||
result = opPred
|
||||
while opPred > limit:
|
||||
while opPred > limit:
|
||||
node = newNode(nkInfix)
|
||||
opNode = newNode(nkIdent, p.tok.literal)
|
||||
getTok(p)
|
||||
@@ -744,8 +744,8 @@ proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
|
||||
node.add(v2)
|
||||
v = node
|
||||
opPred = getPrecedence(p)
|
||||
|
||||
proc parseExpr(p: var SqlParser): SqlNode =
|
||||
|
||||
proc parseExpr(p: var SqlParser): SqlNode =
|
||||
discard lowestExprAux(p, result, - 1)
|
||||
|
||||
proc parseTableName(p: var SqlParser): SqlNode =
|
||||
@@ -765,7 +765,7 @@ proc parseColumnReference(p: var SqlParser): SqlNode =
|
||||
result.add(parseTableName(p))
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc parseCheck(p: var SqlParser): SqlNode =
|
||||
proc parseCheck(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkCheck)
|
||||
result.add(parseExpr(p))
|
||||
@@ -817,9 +817,9 @@ proc parseColumnDef(p: var SqlParser): SqlNode =
|
||||
result.add(newNode(nkIdent, p.tok.literal))
|
||||
getTok(p)
|
||||
result.add(parseDataType(p))
|
||||
parseColumnConstraints(p, result)
|
||||
parseColumnConstraints(p, result)
|
||||
|
||||
proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
|
||||
proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
|
||||
getTok(p)
|
||||
if isKeyw(p, "if"):
|
||||
getTok(p)
|
||||
@@ -880,7 +880,7 @@ proc parseTableDef(p: var SqlParser): SqlNode =
|
||||
result.add(parseTableConstraint(p))
|
||||
if p.tok.kind != tkComma: break
|
||||
eat(p, tkParRi)
|
||||
|
||||
|
||||
proc parseTypeDef(p: var SqlParser): SqlNode =
|
||||
result = parseIfNotExists(p, nkCreateType)
|
||||
expectIdent(p)
|
||||
@@ -966,7 +966,7 @@ proc parseUpdate(p: var SqlParser): SqlNode =
|
||||
result.add(parseWhere(p))
|
||||
else:
|
||||
result.add(nil)
|
||||
|
||||
|
||||
proc parseDelete(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkDelete)
|
||||
@@ -1018,14 +1018,14 @@ proc parseSelect(p: var SqlParser): SqlNode =
|
||||
while true:
|
||||
getTok(p)
|
||||
h.add(parseExpr(p))
|
||||
if p.tok.kind != tkComma: break
|
||||
if p.tok.kind != tkComma: break
|
||||
result.add(h)
|
||||
if isKeyw(p, "union"):
|
||||
result.add(newNode(nkUnion))
|
||||
getTok(p)
|
||||
elif isKeyw(p, "intersect"):
|
||||
result.add(newNode(nkIntersect))
|
||||
getTok(p)
|
||||
getTok(p)
|
||||
elif isKeyw(p, "except"):
|
||||
result.add(newNode(nkExcept))
|
||||
getTok(p)
|
||||
@@ -1083,7 +1083,7 @@ proc open(p: var SqlParser, input: Stream, filename: string) =
|
||||
p.tok.kind = tkInvalid
|
||||
p.tok.literal = ""
|
||||
getTok(p)
|
||||
|
||||
|
||||
proc parse(p: var SqlParser): SqlNode =
|
||||
## parses the content of `p`'s input stream and returns the SQL AST.
|
||||
## Syntax errors raise an `EInvalidSql` exception.
|
||||
@@ -1094,13 +1094,13 @@ proc parse(p: var SqlParser): SqlNode =
|
||||
result.add(s)
|
||||
if result.len == 1:
|
||||
result = result.sons[0]
|
||||
|
||||
|
||||
proc close(p: var SqlParser) =
|
||||
## closes the parser `p`. The associated input stream is closed too.
|
||||
close(SqlLexer(p))
|
||||
|
||||
proc parseSQL*(input: Stream, filename: string): SqlNode =
|
||||
## parses the SQL from `input` into an AST and returns the AST.
|
||||
## parses the SQL from `input` into an AST and returns the AST.
|
||||
## `filename` is only used for error messages.
|
||||
## Syntax errors raise an `EInvalidSql` exception.
|
||||
var p: SqlParser
|
||||
@@ -1114,7 +1114,7 @@ proc ra(n: SqlNode, s: var string, indent: int)
|
||||
|
||||
proc rs(n: SqlNode, s: var string, indent: int,
|
||||
prefix = "(", suffix = ")",
|
||||
sep = ", ") =
|
||||
sep = ", ") =
|
||||
if n.len > 0:
|
||||
s.add(prefix)
|
||||
for i in 0 .. n.len-1:
|
||||
@@ -1162,7 +1162,7 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||
ra(n.sons[1], s, indent)
|
||||
s.add(')')
|
||||
of nkInfix:
|
||||
s.add('(')
|
||||
s.add('(')
|
||||
ra(n.sons[1], s, indent)
|
||||
s.add(' ')
|
||||
ra(n.sons[0], s, indent)
|
||||
@@ -1207,13 +1207,13 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||
s.add("insert into ")
|
||||
ra(n.sons[0], s, indent)
|
||||
ra(n.sons[1], s, indent)
|
||||
if n.sons[2].kind == nkDefault:
|
||||
if n.sons[2].kind == nkDefault:
|
||||
s.add("default values")
|
||||
else:
|
||||
s.add("\nvalues ")
|
||||
ra(n.sons[2], s, indent)
|
||||
s.add(';')
|
||||
of nkUpdate:
|
||||
of nkUpdate:
|
||||
s.add("update ")
|
||||
ra(n.sons[0], s, indent)
|
||||
s.add(" set ")
|
||||
@@ -1225,7 +1225,7 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||
ra(it, s, indent)
|
||||
ra(n.sons[L-1], s, indent)
|
||||
s.add(';')
|
||||
of nkDelete:
|
||||
of nkDelete:
|
||||
s.add("delete from ")
|
||||
ra(n.sons[0], s, indent)
|
||||
ra(n.sons[1], s, indent)
|
||||
@@ -1237,12 +1237,12 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||
rs(n.sons[0], s, indent, "", "", ", ")
|
||||
for i in 1 .. n.len-1: ra(n.sons[i], s, indent)
|
||||
s.add(';')
|
||||
of nkSelectColumns:
|
||||
of nkSelectColumns:
|
||||
assert(false)
|
||||
of nkAsgn:
|
||||
ra(n.sons[0], s, indent)
|
||||
s.add(" = ")
|
||||
ra(n.sons[1], s, indent)
|
||||
ra(n.sons[1], s, indent)
|
||||
of nkFrom:
|
||||
s.add("\nfrom ")
|
||||
rs(n, s, indent, "", "", ", ")
|
||||
@@ -1306,23 +1306,23 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||
s.add("enum ")
|
||||
rs(n, s, indent)
|
||||
|
||||
# What I want:
|
||||
# What I want:
|
||||
#
|
||||
#select(columns = [T1.all, T2.name],
|
||||
#select(columns = [T1.all, T2.name],
|
||||
# fromm = [T1, T2],
|
||||
# where = T1.name ==. T2.name,
|
||||
# orderby = [name]):
|
||||
#
|
||||
#for row in dbQuery(db, """select x, y, z
|
||||
# from a, b
|
||||
#
|
||||
#for row in dbQuery(db, """select x, y, z
|
||||
# from a, b
|
||||
# where a.name = b.name"""):
|
||||
#
|
||||
#
|
||||
|
||||
#select x, y, z:
|
||||
# fromm: Table1, Table2
|
||||
# where: x.name == y.name
|
||||
#db.select(fromm = [t1, t2], where = t1.name == t2.name):
|
||||
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
|
||||
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
|
||||
# writeLine x, y, z
|
||||
|
||||
proc renderSQL*(n: SqlNode): string =
|
||||
@@ -1338,7 +1338,7 @@ when not defined(testing) and isMainModule:
|
||||
happiness happiness
|
||||
);
|
||||
CREATE INDEX table1_attr1 ON table1(attr1);
|
||||
|
||||
|
||||
SELECT * FROM myTab WHERE col1 = 'happy';
|
||||
"""), "stdin")))
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import strutils
|
||||
type
|
||||
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
|
||||
## any optional component is "" if it does not exist
|
||||
scheme, username, password,
|
||||
scheme, username, password,
|
||||
hostname, port, path, query, anchor: string]
|
||||
|
||||
{.deprecated: [TUrl: Url].}
|
||||
@@ -31,7 +31,7 @@ proc parseUrl*(url: string): Url {.deprecated.} =
|
||||
var hostname, port, path, query, anchor: string = ""
|
||||
|
||||
var temp = ""
|
||||
|
||||
|
||||
if url[i] != '/': # url isn't a relative path
|
||||
while true:
|
||||
# Scheme
|
||||
@@ -48,7 +48,7 @@ proc parseUrl*(url: string): Url {.deprecated.} =
|
||||
password = username.substr(colon+1)
|
||||
username = username.substr(0, colon-1)
|
||||
temp.setLen(0)
|
||||
inc(i) #Skip the @
|
||||
inc(i) #Skip the @
|
||||
# hostname(subdomain, domain, port)
|
||||
if url[i] == '/' or url[i] == '\0':
|
||||
hostname = temp
|
||||
@@ -56,10 +56,10 @@ proc parseUrl*(url: string): Url {.deprecated.} =
|
||||
if colon >= 0:
|
||||
port = hostname.substr(colon+1)
|
||||
hostname = hostname.substr(0, colon-1)
|
||||
|
||||
|
||||
temp.setLen(0)
|
||||
break
|
||||
|
||||
|
||||
temp.add(url[i])
|
||||
inc(i)
|
||||
|
||||
@@ -75,7 +75,7 @@ proc parseUrl*(url: string): Url {.deprecated.} =
|
||||
else:
|
||||
path = temp
|
||||
temp.setLen(0)
|
||||
|
||||
|
||||
if url[i] == '\0':
|
||||
if temp[0] == '?':
|
||||
query = temp
|
||||
@@ -84,10 +84,10 @@ proc parseUrl*(url: string): Url {.deprecated.} =
|
||||
else:
|
||||
path = temp
|
||||
break
|
||||
|
||||
|
||||
temp.add(url[i])
|
||||
inc(i)
|
||||
|
||||
|
||||
return (scheme, username, password, hostname, port, path, query, anchor)
|
||||
|
||||
proc `$`*(u: Url): string {.deprecated.} =
|
||||
@@ -103,12 +103,12 @@ proc `$`*(u: Url): string {.deprecated.} =
|
||||
result.add(u.password)
|
||||
result.add("@")
|
||||
result.add(u.hostname)
|
||||
if u.port.len > 0:
|
||||
if u.port.len > 0:
|
||||
result.add(":")
|
||||
result.add(u.port)
|
||||
if u.path.len > 0:
|
||||
if u.path.len > 0:
|
||||
result.add("/")
|
||||
result.add(u.path)
|
||||
result.add(u.query)
|
||||
result.add(u.anchor)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import math
|
||||
import strutils
|
||||
import numeric
|
||||
|
||||
type
|
||||
type
|
||||
Poly* = object
|
||||
cofs:seq[float]
|
||||
|
||||
@@ -40,7 +40,7 @@ proc `[]` *(p:Poly;idx:int):float=
|
||||
if idx<0 or idx>p.degree:
|
||||
return 0.0
|
||||
return p.cofs[idx]
|
||||
|
||||
|
||||
proc `[]=` *(p:var Poly;idx:int,v:float)=
|
||||
## Sets an coefficient of the polynomial by index.
|
||||
## p[2] set the quadric term, p[3] the cubic etc.
|
||||
@@ -55,15 +55,15 @@ proc `[]=` *(p:var Poly;idx:int,v:float)=
|
||||
p.cofs[q]=0.0 #new-grown coefficients set to zero
|
||||
|
||||
p.cofs[idx]=v
|
||||
|
||||
|
||||
|
||||
|
||||
iterator items*(p:Poly):float=
|
||||
## Iterates through the coefficients of the polynomial.
|
||||
var i=p.degree
|
||||
while i>=0:
|
||||
yield p[i]
|
||||
dec i
|
||||
|
||||
dec i
|
||||
|
||||
proc clean*(p:var Poly;zerotol=0.0)=
|
||||
## Removes leading zero coefficients of the polynomial.
|
||||
## An optional tolerance can be given for what's considered zero.
|
||||
@@ -77,19 +77,19 @@ proc clean*(p:var Poly;zerotol=0.0)=
|
||||
if relen: p.cofs.setLen(n+1)
|
||||
|
||||
|
||||
proc `$` *(p:Poly):string =
|
||||
proc `$` *(p:Poly):string =
|
||||
## Gets a somewhat reasonable string representation of the polynomial
|
||||
## The format should be compatible with most online function plotters,
|
||||
## for example directly in google search
|
||||
result=""
|
||||
var first=true #might skip + sign if first coefficient
|
||||
|
||||
|
||||
for idx in countdown(p.degree,0):
|
||||
let a=p[idx]
|
||||
|
||||
|
||||
if a==0.0:
|
||||
continue
|
||||
|
||||
|
||||
if a>= 0.0 and not first:
|
||||
result.add('+')
|
||||
first=false
|
||||
@@ -103,14 +103,14 @@ proc `$` *(p:Poly):string =
|
||||
|
||||
if result=="":
|
||||
result="0"
|
||||
|
||||
|
||||
|
||||
proc derivative*(p: Poly): Poly=
|
||||
## Returns a new polynomial, which is the derivative of `p`
|
||||
newSeq[float](result.cofs,p.degree)
|
||||
for idx in 0..high(result.cofs):
|
||||
result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
|
||||
|
||||
|
||||
proc diff*(p:Poly,x:float):float=
|
||||
## Evaluates the differentiation of a polynomial with
|
||||
## respect to `x` quickly using a modifed Horners method
|
||||
@@ -128,7 +128,7 @@ proc integral*(p:Poly):Poly=
|
||||
result.cofs[0]=0.0 #constant arbitrary term, use 0.0
|
||||
for i in 1..high(result.cofs):
|
||||
result.cofs[i]=p.cofs[i-1]/float(i)
|
||||
|
||||
|
||||
|
||||
proc integrate*(p:Poly;xmin,xmax:float):float=
|
||||
## Computes the definite integral of `p` between `xmin` and `xmax`
|
||||
@@ -145,9 +145,9 @@ proc integrate*(p:Poly;xmin,xmax:float):float=
|
||||
s1 = s1*xmin+fac
|
||||
s2 = s2*xmax+fac
|
||||
dec n
|
||||
|
||||
|
||||
result=s2*xmax-s1*xmin
|
||||
|
||||
|
||||
proc initPoly*(cofs:varargs[float]):Poly=
|
||||
## Initializes a polynomial with given coefficients.
|
||||
## The most significant coefficient is first, so to create x^2-2x+3:
|
||||
@@ -158,7 +158,7 @@ proc initPoly*(cofs:varargs[float]):Poly=
|
||||
# reverse order of coefficients so indexing matches degree of
|
||||
# coefficient...
|
||||
result.cofs= @[]
|
||||
for idx in countdown(cofs.len-1,0):
|
||||
for idx in countdown(cofs.len-1,0):
|
||||
result.cofs.add(cofs[idx])
|
||||
|
||||
result.clean #remove leading zero terms
|
||||
@@ -167,49 +167,49 @@ proc initPoly*(cofs:varargs[float]):Poly=
|
||||
proc divMod*(p,d:Poly;q,r:var Poly)=
|
||||
## Divides `p` with `d`, and stores the quotinent in `q` and
|
||||
## the remainder in `d`
|
||||
var
|
||||
var
|
||||
pdeg=p.degree
|
||||
ddeg=d.degree
|
||||
power=p.degree-d.degree
|
||||
ratio:float
|
||||
|
||||
|
||||
r.cofs = p.cofs #initial remainder=numerator
|
||||
if power<0: #denominator is larger than numerator
|
||||
q.cofs= @ [0.0] #quotinent is 0.0
|
||||
return # keep remainder as numerator
|
||||
|
||||
|
||||
q.cofs=newSeq[float](power+1)
|
||||
|
||||
|
||||
for i in countdown(pdeg,ddeg):
|
||||
ratio=r.cofs[i]/d.cofs[ddeg]
|
||||
|
||||
|
||||
q.cofs[i-ddeg]=ratio
|
||||
r.cofs[i]=0.0
|
||||
|
||||
|
||||
for j in countup(0,<ddeg):
|
||||
var idx=i-ddeg+j
|
||||
r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
|
||||
|
||||
|
||||
r.clean # drop zero coefficients in remainder
|
||||
|
||||
proc `+` *(p1:Poly,p2:Poly):Poly=
|
||||
## Adds two polynomials
|
||||
var n=max(p1.cofs.len,p2.cofs.len)
|
||||
newSeq(result.cofs,n)
|
||||
|
||||
|
||||
for idx in countup(0,n-1):
|
||||
result[idx]=p1[idx]+p2[idx]
|
||||
|
||||
|
||||
result.clean # drop zero coefficients in remainder
|
||||
|
||||
|
||||
proc `*` *(p1:Poly,p2:Poly):Poly=
|
||||
## Multiplies the polynomial `p1` with `p2`
|
||||
var
|
||||
var
|
||||
d1=p1.degree
|
||||
d2=p2.degree
|
||||
n=d1+d2
|
||||
idx:int
|
||||
|
||||
|
||||
newSeq(result.cofs,n)
|
||||
|
||||
for i1 in countup(0,d1):
|
||||
@@ -225,38 +225,38 @@ proc `*` *(p:Poly,f:float):Poly=
|
||||
for i in 0..high(p.cofs):
|
||||
result[i]=p.cofs[i]*f
|
||||
result.clean
|
||||
|
||||
|
||||
proc `*` *(f:float,p:Poly):Poly=
|
||||
## Multiplies a real number with a polynomial
|
||||
return p*f
|
||||
|
||||
|
||||
proc `-`*(p:Poly):Poly=
|
||||
## Negates a polynomial
|
||||
result=p
|
||||
for i in countup(0,<result.cofs.len):
|
||||
result.cofs[i]= -result.cofs[i]
|
||||
|
||||
|
||||
proc `-` *(p1:Poly,p2:Poly):Poly=
|
||||
## Subtract `p1` with `p2`
|
||||
var n=max(p1.cofs.len,p2.cofs.len)
|
||||
newSeq(result.cofs,n)
|
||||
|
||||
|
||||
for idx in countup(0,n-1):
|
||||
result[idx]=p1[idx]-p2[idx]
|
||||
|
||||
|
||||
result.clean # drop zero coefficients in remainder
|
||||
|
||||
|
||||
proc `/`*(p:Poly,f:float):Poly=
|
||||
## Divides polynomial `p` with a real number `f`
|
||||
newSeq(result.cofs,p.cofs.len)
|
||||
for i in 0..high(p.cofs):
|
||||
result[i]=p.cofs[i]/f
|
||||
result.clean
|
||||
|
||||
|
||||
proc `/` *(p,q:Poly):Poly=
|
||||
## Divides polynomial `p` with polynomial `q`
|
||||
var dummy:Poly
|
||||
p.divMod(q,result,dummy)
|
||||
p.divMod(q,result,dummy)
|
||||
|
||||
proc `mod` *(p,q:Poly):Poly=
|
||||
## Computes the polynomial modulo operation,
|
||||
@@ -277,20 +277,20 @@ proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
|
||||
## Solves the quadric equation `ax^2+bx+c`, with a possible
|
||||
## tolerance `zerotol` to find roots of curves just 'touching'
|
||||
## the x axis. Returns sequence with 0,1 or 2 solutions.
|
||||
|
||||
|
||||
var p,q,d:float
|
||||
|
||||
|
||||
p=b/(2.0*a)
|
||||
|
||||
|
||||
if p==Inf or p==NegInf: #linear equation..
|
||||
var linrt= -c/b
|
||||
if linrt==Inf or linrt==NegInf: #constant only
|
||||
return @[]
|
||||
return @[linrt]
|
||||
|
||||
|
||||
q=c/a
|
||||
d=p*p-q
|
||||
|
||||
|
||||
if d<0.0:
|
||||
#check for inside zerotol range for neg. roots
|
||||
var err=a*p*p-b*p+c #evaluate error at parabola center axis
|
||||
@@ -309,12 +309,12 @@ proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
|
||||
var deg=p.degree
|
||||
var d=p[deg]
|
||||
var bound1,bound2:float
|
||||
|
||||
|
||||
for i in countup(0,deg):
|
||||
var c=abs(p.cofs[i]/d)
|
||||
bound1=max(bound1,c+1.0)
|
||||
bound2=bound2+c
|
||||
|
||||
|
||||
bound2=max(1.0,bound2)
|
||||
result.xmax=min(bound1,bound2)
|
||||
result.xmin= -result.xmax
|
||||
@@ -327,13 +327,13 @@ proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxite
|
||||
var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
|
||||
if br.success:
|
||||
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
|
||||
res.add(br.rootx)
|
||||
res.add(br.rootx)
|
||||
else:
|
||||
#this might be a 'touching' case, check function value against
|
||||
#zero tolerance
|
||||
if abs(br.rooty)<=zerotol:
|
||||
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
|
||||
res.add(br.rootx)
|
||||
res.add(br.rootx)
|
||||
|
||||
|
||||
proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
|
||||
|
||||
@@ -216,7 +216,7 @@ proc hash*[T](x: Rational[T]): Hash =
|
||||
h = h !& hash(copy.num)
|
||||
h = h !& hash(copy.den)
|
||||
result = !$h
|
||||
|
||||
|
||||
when isMainModule:
|
||||
var
|
||||
z = Rational[int](num: 0, den: 1)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## redis-server instance, send commands and receive replies.
|
||||
##
|
||||
## **Beware**: Most (if not all) functions that return a ``RedisString`` may
|
||||
## return ``redisNil``, and functions which return a ``RedisList``
|
||||
## return ``redisNil``, and functions which return a ``RedisList``
|
||||
## may return ``nil``.
|
||||
|
||||
import sockets, os, strutils, parseutils
|
||||
@@ -19,7 +19,7 @@ import sockets, os, strutils, parseutils
|
||||
const
|
||||
redisNil* = "\0\0"
|
||||
|
||||
type
|
||||
type
|
||||
Pipeline = ref object
|
||||
enabled: bool
|
||||
buffer: string
|
||||
@@ -34,7 +34,7 @@ type
|
||||
socket: Socket
|
||||
connected: bool
|
||||
pipeline: Pipeline
|
||||
|
||||
|
||||
RedisStatus* = string
|
||||
RedisInteger* = BiggestInt
|
||||
RedisString* = string ## Bulk reply
|
||||
@@ -59,10 +59,10 @@ proc open*(host = "localhost", port = 6379.Port): Redis =
|
||||
if result.socket == invalidSocket:
|
||||
raiseOSError(osLastError())
|
||||
result.socket.connect(host, port)
|
||||
result.pipeline = newPipeline()
|
||||
result.pipeline = newPipeline()
|
||||
|
||||
proc raiseInvalidReply(expected, got: char) =
|
||||
raise newException(ReplyError,
|
||||
raise newException(ReplyError,
|
||||
"Expected '$1' at the beginning of a status reply got '$2'" %
|
||||
[$expected, $got])
|
||||
|
||||
@@ -90,16 +90,16 @@ proc parseStatus(r: Redis, line: string = ""): RedisStatus =
|
||||
raise newException(RedisError, strip(line))
|
||||
if line[0] != '+':
|
||||
raiseInvalidReply('+', line[0])
|
||||
|
||||
|
||||
return line.substr(1) # Strip '+'
|
||||
|
||||
proc readStatus(r:Redis): RedisStatus =
|
||||
r.readSocket("PIPELINED")
|
||||
return r.parseStatus(line)
|
||||
|
||||
|
||||
proc parseInteger(r: Redis, line: string = ""): RedisInteger =
|
||||
if r.pipeline.enabled: return -1
|
||||
|
||||
|
||||
#if line == "+QUEUED": # inside of multi
|
||||
# return -1
|
||||
|
||||
@@ -110,10 +110,10 @@ proc parseInteger(r: Redis, line: string = ""): RedisInteger =
|
||||
raise newException(RedisError, strip(line))
|
||||
if line[0] != ':':
|
||||
raiseInvalidReply(':', line[0])
|
||||
|
||||
|
||||
# Strip ':'
|
||||
if parseBiggestInt(line, result, 1) == 0:
|
||||
raise newException(ReplyError, "Unable to parse integer.")
|
||||
raise newException(ReplyError, "Unable to parse integer.")
|
||||
|
||||
proc readInteger(r: Redis): RedisInteger =
|
||||
r.readSocket(-1)
|
||||
@@ -126,19 +126,19 @@ proc recv(sock: Socket, size: int): TaintedString =
|
||||
|
||||
proc parseSingleString(r: Redis, line:string, allowMBNil = false): RedisString =
|
||||
if r.pipeline.enabled: return ""
|
||||
|
||||
|
||||
# Error.
|
||||
if line[0] == '-':
|
||||
raise newException(RedisError, strip(line))
|
||||
|
||||
|
||||
# Some commands return a /bulk/ value or a /multi-bulk/ nil. Odd.
|
||||
if allowMBNil:
|
||||
if line == "*-1":
|
||||
return redisNil
|
||||
|
||||
|
||||
if line[0] != '$':
|
||||
raiseInvalidReply('$', line[0])
|
||||
|
||||
|
||||
var numBytes = parseInt(line.substr(1))
|
||||
if numBytes == -1:
|
||||
return redisNil
|
||||
@@ -168,7 +168,7 @@ proc parseArrayLines(r: Redis, countLine:string): RedisList =
|
||||
|
||||
proc readArrayLines(r: Redis): RedisList =
|
||||
r.readSocket(nil)
|
||||
return r.parseArrayLines(line)
|
||||
return r.parseArrayLines(line)
|
||||
|
||||
proc parseBulkString(r: Redis, allowMBNil = false, line:string = ""): RedisString =
|
||||
if r.pipeline.enabled: return ""
|
||||
@@ -191,7 +191,7 @@ proc readNext(r: Redis): RedisList =
|
||||
of ':': @[$(r.parseInteger(line))]
|
||||
of '$': @[r.parseBulkString(true,line)]
|
||||
of '*': r.parseArrayLines(line)
|
||||
else:
|
||||
else:
|
||||
raise newException(ReplyError, "readNext failed on line: " & line)
|
||||
nil
|
||||
r.pipeline.expected -= 1
|
||||
@@ -202,10 +202,10 @@ proc flushPipeline*(r: Redis, wasMulti = false): RedisList =
|
||||
if r.pipeline.buffer.len > 0:
|
||||
r.socket.send(r.pipeline.buffer)
|
||||
r.pipeline.buffer = ""
|
||||
|
||||
|
||||
r.pipeline.enabled = false
|
||||
result = @[]
|
||||
|
||||
|
||||
var tot = r.pipeline.expected
|
||||
|
||||
for i in 0..tot-1:
|
||||
@@ -232,7 +232,7 @@ proc sendCommand(r: Redis, cmd: string, args: varargs[string]) =
|
||||
for i in items(args):
|
||||
request.add("$" & $i.len() & "\c\L")
|
||||
request.add(i & "\c\L")
|
||||
|
||||
|
||||
if r.pipeline.enabled:
|
||||
r.pipeline.buffer.add(request)
|
||||
r.pipeline.expected += 1
|
||||
@@ -249,7 +249,7 @@ proc sendCommand(r: Redis, cmd: string, arg1: string,
|
||||
for i in items(args):
|
||||
request.add("$" & $i.len() & "\c\L")
|
||||
request.add(i & "\c\L")
|
||||
|
||||
|
||||
if r.pipeline.enabled:
|
||||
r.pipeline.expected += 1
|
||||
r.pipeline.buffer.add(request)
|
||||
@@ -275,7 +275,7 @@ proc expire*(r: Redis, key: string, seconds: int): bool =
|
||||
return r.readInteger() == 1
|
||||
|
||||
proc expireAt*(r: Redis, key: string, timestamp: int): bool =
|
||||
## Set the expiration for a key as a UNIX timestamp. Returns `false`
|
||||
## Set the expiration for a key as a UNIX timestamp. Returns `false`
|
||||
## if the key could not be found or the timeout could not be set.
|
||||
r.sendCommand("EXPIREAT", key, $timestamp)
|
||||
return r.readInteger() == 1
|
||||
@@ -301,7 +301,7 @@ proc scan*(r: Redis, cursor: var BiggestInt, pattern: string): RedisList =
|
||||
cursor = strutils.parseBiggestInt(reply[0])
|
||||
return reply[1..high(reply)]
|
||||
|
||||
proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList =
|
||||
proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList =
|
||||
## Find all keys matching the given pattern and yield it to client in portions
|
||||
## using cursor as a client query identifier.
|
||||
r.sendCommand("SCAN", $cursor, ["MATCH", pattern, "COUNT", $count])
|
||||
@@ -315,11 +315,11 @@ proc move*(r: Redis, key: string, db: int): bool =
|
||||
return r.readInteger() == 1
|
||||
|
||||
proc persist*(r: Redis, key: string): bool =
|
||||
## Remove the expiration from a key.
|
||||
## Remove the expiration from a key.
|
||||
## Returns `true` when the timeout was removed.
|
||||
r.sendCommand("PERSIST", key)
|
||||
return r.readInteger() == 1
|
||||
|
||||
|
||||
proc randomKey*(r: Redis): RedisString =
|
||||
## Return a random key from the keyspace
|
||||
r.sendCommand("RANDOMKEY")
|
||||
@@ -327,11 +327,11 @@ proc randomKey*(r: Redis): RedisString =
|
||||
|
||||
proc rename*(r: Redis, key, newkey: string): RedisStatus =
|
||||
## Rename a key.
|
||||
##
|
||||
##
|
||||
## **WARNING:** Overwrites `newkey` if it exists!
|
||||
r.sendCommand("RENAME", key, newkey)
|
||||
raiseNoOK(r.readStatus(), r.pipeline.enabled)
|
||||
|
||||
|
||||
proc renameNX*(r: Redis, key, newkey: string): bool =
|
||||
## Same as ``rename`` but doesn't continue if `newkey` exists.
|
||||
## Returns `true` if key was renamed.
|
||||
@@ -342,12 +342,12 @@ proc ttl*(r: Redis, key: string): RedisInteger =
|
||||
## Get the time to live for a key
|
||||
r.sendCommand("TTL", key)
|
||||
return r.readInteger()
|
||||
|
||||
|
||||
proc keyType*(r: Redis, key: string): RedisStatus =
|
||||
## Determine the type stored at key
|
||||
r.sendCommand("TYPE", key)
|
||||
return r.readStatus()
|
||||
|
||||
|
||||
|
||||
# Strings
|
||||
|
||||
@@ -360,12 +360,12 @@ proc decr*(r: Redis, key: string): RedisInteger =
|
||||
## Decrement the integer value of a key by one
|
||||
r.sendCommand("DECR", key)
|
||||
return r.readInteger()
|
||||
|
||||
|
||||
proc decrBy*(r: Redis, key: string, decrement: int): RedisInteger =
|
||||
## Decrement the integer value of a key by the given number
|
||||
r.sendCommand("DECRBY", key, $decrement)
|
||||
return r.readInteger()
|
||||
|
||||
|
||||
proc get*(r: Redis, key: string): RedisString =
|
||||
## Get the value of a key. Returns `redisNil` when `key` doesn't exist.
|
||||
r.sendCommand("GET", key)
|
||||
@@ -397,7 +397,7 @@ proc incrBy*(r: Redis, key: string, increment: int): RedisInteger =
|
||||
r.sendCommand("INCRBY", key, $increment)
|
||||
return r.readInteger()
|
||||
|
||||
proc setk*(r: Redis, key, value: string) =
|
||||
proc setk*(r: Redis, key, value: string) =
|
||||
## Set the string value of a key.
|
||||
##
|
||||
## NOTE: This function had to be renamed due to a clash with the `set` type.
|
||||
@@ -410,18 +410,18 @@ proc setNX*(r: Redis, key, value: string): bool =
|
||||
r.sendCommand("SETNX", key, value)
|
||||
return r.readInteger() == 1
|
||||
|
||||
proc setBit*(r: Redis, key: string, offset: int,
|
||||
proc setBit*(r: Redis, key: string, offset: int,
|
||||
value: string): RedisInteger =
|
||||
## Sets or clears the bit at offset in the string value stored at key
|
||||
r.sendCommand("SETBIT", key, $offset, value)
|
||||
return r.readInteger()
|
||||
|
||||
|
||||
proc setEx*(r: Redis, key: string, seconds: int, value: string): RedisStatus =
|
||||
## Set the value and expiration of a key
|
||||
r.sendCommand("SETEX", key, $seconds, value)
|
||||
raiseNoOK(r.readStatus(), r.pipeline.enabled)
|
||||
|
||||
proc setRange*(r: Redis, key: string, offset: int,
|
||||
proc setRange*(r: Redis, key: string, offset: int,
|
||||
value: string): RedisInteger =
|
||||
## Overwrite part of a string at key starting at the specified offset
|
||||
r.sendCommand("SETRANGE", key, $offset, value)
|
||||
@@ -474,7 +474,7 @@ proc hMGet*(r: Redis, key: string, fields: varargs[string]): RedisList =
|
||||
r.sendCommand("HMGET", key, fields)
|
||||
return r.readArray()
|
||||
|
||||
proc hMSet*(r: Redis, key: string,
|
||||
proc hMSet*(r: Redis, key: string,
|
||||
fieldValues: openArray[tuple[field, value: string]]) =
|
||||
## Set multiple hash fields to multiple values
|
||||
var args = @[key]
|
||||
@@ -488,7 +488,7 @@ proc hSet*(r: Redis, key, field, value: string): RedisInteger =
|
||||
## Set the string value of a hash field
|
||||
r.sendCommand("HSET", key, field, value)
|
||||
return r.readInteger()
|
||||
|
||||
|
||||
proc hSetNX*(r: Redis, key, field, value: string): RedisInteger =
|
||||
## Set the value of a hash field, only if the field does **not** exist
|
||||
r.sendCommand("HSETNX", key, field, value)
|
||||
@@ -498,11 +498,11 @@ proc hVals*(r: Redis, key: string): RedisList =
|
||||
## Get all the values in a hash
|
||||
r.sendCommand("HVALS", key)
|
||||
return r.readArray()
|
||||
|
||||
|
||||
# Lists
|
||||
|
||||
proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
|
||||
## Remove and get the *first* element in a list, or block until
|
||||
## Remove and get the *first* element in a list, or block until
|
||||
## one is available
|
||||
var args: seq[string] = @[]
|
||||
for i in items(keys): args.add(i)
|
||||
@@ -511,7 +511,7 @@ proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
|
||||
return r.readArray()
|
||||
|
||||
proc bRPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
|
||||
## Remove and get the *last* element in a list, or block until one
|
||||
## Remove and get the *last* element in a list, or block until one
|
||||
## is available.
|
||||
var args: seq[string] = @[]
|
||||
for i in items(keys): args.add(i)
|
||||
@@ -539,7 +539,7 @@ proc lInsert*(r: Redis, key: string, before: bool, pivot, value: string):
|
||||
var pos = if before: "BEFORE" else: "AFTER"
|
||||
r.sendCommand("LINSERT", key, pos, pivot, value)
|
||||
return r.readInteger()
|
||||
|
||||
|
||||
proc lLen*(r: Redis, key: string): RedisInteger =
|
||||
## Get the length of a list
|
||||
r.sendCommand("LLEN", key)
|
||||
@@ -553,7 +553,7 @@ proc lPop*(r: Redis, key: string): RedisString =
|
||||
proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
|
||||
## Prepend a value to a list. Returns the length of the list after the push.
|
||||
## The ``create`` param specifies whether a list should be created if it
|
||||
## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH`
|
||||
## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH`
|
||||
## will be used, otherwise `LPUSHX`.
|
||||
if create:
|
||||
r.sendCommand("LPUSH", key, value)
|
||||
@@ -562,7 +562,7 @@ proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
|
||||
return r.readInteger()
|
||||
|
||||
proc lRange*(r: Redis, key: string, start, stop: int): RedisList =
|
||||
## Get a range of elements from a list. Returns `nil` when `key`
|
||||
## Get a range of elements from a list. Returns `nil` when `key`
|
||||
## doesn't exist.
|
||||
r.sendCommand("LRANGE", key, $start, $stop)
|
||||
return r.readArray()
|
||||
@@ -587,16 +587,16 @@ proc rPop*(r: Redis, key: string): RedisString =
|
||||
## Remove and get the last element in a list
|
||||
r.sendCommand("RPOP", key)
|
||||
return r.readBulkString()
|
||||
|
||||
|
||||
proc rPopLPush*(r: Redis, source, destination: string): RedisString =
|
||||
## Remove the last element in a list, append it to another list and return it
|
||||
r.sendCommand("RPOPLPUSH", source, destination)
|
||||
return r.readBulkString()
|
||||
|
||||
|
||||
proc rPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
|
||||
## Append a value to a list. Returns the length of the list after the push.
|
||||
## The ``create`` param specifies whether a list should be created if it
|
||||
## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH`
|
||||
## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH`
|
||||
## will be used, otherwise `RPUSHX`.
|
||||
if create:
|
||||
r.sendCommand("RPUSH", key, value)
|
||||
@@ -676,7 +676,7 @@ proc sunion*(r: Redis, keys: varargs[string]): RedisList =
|
||||
|
||||
proc sunionstore*(r: Redis, destination: string,
|
||||
key: varargs[string]): RedisInteger =
|
||||
## Add multiple sets and store the resulting set in a key
|
||||
## Add multiple sets and store the resulting set in a key
|
||||
r.sendCommand("SUNIONSTORE", destination, key)
|
||||
return r.readInteger()
|
||||
|
||||
@@ -710,16 +710,16 @@ proc zinterstore*(r: Redis, destination: string, numkeys: string,
|
||||
## a new key
|
||||
var args = @[destination, numkeys]
|
||||
for i in items(keys): args.add(i)
|
||||
|
||||
|
||||
if weights.len != 0:
|
||||
args.add("WITHSCORE")
|
||||
for i in items(weights): args.add(i)
|
||||
if aggregate.len != 0:
|
||||
args.add("AGGREGATE")
|
||||
args.add(aggregate)
|
||||
|
||||
|
||||
r.sendCommand("ZINTERSTORE", args)
|
||||
|
||||
|
||||
return r.readInteger()
|
||||
|
||||
proc zrange*(r: Redis, key: string, start: string, stop: string,
|
||||
@@ -731,18 +731,18 @@ proc zrange*(r: Redis, key: string, start: string, stop: string,
|
||||
r.sendCommand("ZRANGE", "WITHSCORES", key, start, stop)
|
||||
return r.readArray()
|
||||
|
||||
proc zrangebyscore*(r: Redis, key: string, min: string, max: string,
|
||||
proc zrangebyscore*(r: Redis, key: string, min: string, max: string,
|
||||
withScore: bool = false, limit: bool = false,
|
||||
limitOffset: int = 0, limitCount: int = 0): RedisList =
|
||||
## Return a range of members in a sorted set, by score
|
||||
var args = @[key, min, max]
|
||||
|
||||
|
||||
if withScore: args.add("WITHSCORE")
|
||||
if limit:
|
||||
if limit:
|
||||
args.add("LIMIT")
|
||||
args.add($limitOffset)
|
||||
args.add($limitCount)
|
||||
|
||||
|
||||
r.sendCommand("ZRANGEBYSCORE", args)
|
||||
return r.readArray()
|
||||
|
||||
@@ -770,26 +770,26 @@ proc zremrangebyscore*(r: Redis, key: string, min: string,
|
||||
|
||||
proc zrevrange*(r: Redis, key: string, start: string, stop: string,
|
||||
withScore: bool): RedisList =
|
||||
## Return a range of members in a sorted set, by index,
|
||||
## Return a range of members in a sorted set, by index,
|
||||
## with scores ordered from high to low
|
||||
if withScore:
|
||||
r.sendCommand("ZREVRANGE", "WITHSCORE", key, start, stop)
|
||||
else: r.sendCommand("ZREVRANGE", key, start, stop)
|
||||
return r.readArray()
|
||||
|
||||
proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string,
|
||||
proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string,
|
||||
withScore: bool = false, limit: bool = false,
|
||||
limitOffset: int = 0, limitCount: int = 0): RedisList =
|
||||
## Return a range of members in a sorted set, by score, with
|
||||
## scores ordered from high to low
|
||||
var args = @[key, min, max]
|
||||
|
||||
|
||||
if withScore: args.add("WITHSCORE")
|
||||
if limit:
|
||||
if limit:
|
||||
args.add("LIMIT")
|
||||
args.add($limitOffset)
|
||||
args.add($limitCount)
|
||||
|
||||
|
||||
r.sendCommand("ZREVRANGEBYSCORE", args)
|
||||
return r.readArray()
|
||||
|
||||
@@ -807,24 +807,24 @@ proc zscore*(r: Redis, key: string, member: string): RedisString =
|
||||
proc zunionstore*(r: Redis, destination: string, numkeys: string,
|
||||
keys: openArray[string], weights: openArray[string] = [],
|
||||
aggregate: string = ""): RedisInteger =
|
||||
## Add multiple sorted sets and store the resulting sorted set in a new key
|
||||
## Add multiple sorted sets and store the resulting sorted set in a new key
|
||||
var args = @[destination, numkeys]
|
||||
for i in items(keys): args.add(i)
|
||||
|
||||
|
||||
if weights.len != 0:
|
||||
args.add("WEIGHTS")
|
||||
for i in items(weights): args.add(i)
|
||||
if aggregate.len != 0:
|
||||
args.add("AGGREGATE")
|
||||
args.add(aggregate)
|
||||
|
||||
|
||||
r.sendCommand("ZUNIONSTORE", args)
|
||||
|
||||
|
||||
return r.readInteger()
|
||||
|
||||
# HyperLogLog
|
||||
|
||||
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
|
||||
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
|
||||
## Add variable number of elements into special 'HyperLogLog' set type
|
||||
r.sendCommand("PFADD", key, elements)
|
||||
return r.readInteger()
|
||||
@@ -864,7 +864,7 @@ proc subscribe*(r: Redis, channel: openarray[string]): ???? =
|
||||
return ???
|
||||
|
||||
proc unsubscribe*(r: Redis, [channel: openarray[string], : string): ???? =
|
||||
## Stop listening for messages posted to the given channels
|
||||
## Stop listening for messages posted to the given channels
|
||||
r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ])
|
||||
return ???
|
||||
|
||||
@@ -879,12 +879,12 @@ proc discardMulti*(r: Redis) =
|
||||
|
||||
proc exec*(r: Redis): RedisList =
|
||||
## Execute all commands issued after MULTI
|
||||
r.sendCommand("EXEC")
|
||||
r.sendCommand("EXEC")
|
||||
r.pipeline.enabled = false
|
||||
# Will reply with +OK for MULTI/EXEC and +QUEUED for every command
|
||||
# between, then with the results
|
||||
return r.flushPipeline(true)
|
||||
|
||||
|
||||
|
||||
proc multi*(r: Redis) =
|
||||
## Mark the start of a transaction block
|
||||
@@ -898,7 +898,7 @@ proc unwatch*(r: Redis) =
|
||||
raiseNoOK(r.readStatus(), r.pipeline.enabled)
|
||||
|
||||
proc watch*(r: Redis, key: varargs[string]) =
|
||||
## Watch the given keys to determine execution of the MULTI/EXEC block
|
||||
## Watch the given keys to determine execution of the MULTI/EXEC block
|
||||
r.sendCommand("WATCH", key)
|
||||
raiseNoOK(r.readStatus(), r.pipeline.enabled)
|
||||
|
||||
@@ -925,7 +925,7 @@ proc quit*(r: Redis) =
|
||||
raiseNoOK(r.readStatus(), r.pipeline.enabled)
|
||||
|
||||
proc select*(r: Redis, index: int): RedisStatus =
|
||||
## Change the selected database for the current connection
|
||||
## Change the selected database for the current connection
|
||||
r.sendCommand("SELECT", $index)
|
||||
return r.readStatus()
|
||||
|
||||
@@ -1016,7 +1016,7 @@ proc slaveof*(r: Redis, host: string, port: string) =
|
||||
|
||||
iterator hPairs*(r: Redis, key: string): tuple[key, value: string] =
|
||||
## Iterator for keys and values in a hash.
|
||||
var
|
||||
var
|
||||
contents = r.hGetAll(key)
|
||||
k = ""
|
||||
for i in items(contents):
|
||||
@@ -1031,9 +1031,9 @@ proc someTests(r: Redis, how: SendMode):seq[string] =
|
||||
|
||||
if how == pipelined:
|
||||
r.startPipelining()
|
||||
elif how == multiple:
|
||||
elif how == multiple:
|
||||
r.multi()
|
||||
|
||||
|
||||
r.setk("nim:test", "Testing something.")
|
||||
r.setk("nim:utf8", "こんにちは")
|
||||
r.setk("nim:esc", "\\ths ągt\\")
|
||||
@@ -1054,7 +1054,7 @@ proc someTests(r: Redis, how: SendMode):seq[string] =
|
||||
|
||||
for i in items(p):
|
||||
if not isNil(i):
|
||||
list.add(i)
|
||||
list.add(i)
|
||||
|
||||
list.add(r.debugObject("mylist"))
|
||||
|
||||
@@ -1079,7 +1079,7 @@ proc assertListsIdentical(listA, listB: seq[string]) =
|
||||
for item in listA:
|
||||
assert(item == listB[i])
|
||||
i = i + 1
|
||||
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
when false:
|
||||
var r = open()
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
|
||||
|
||||
const
|
||||
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
||||
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
||||
'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of
|
||||
|
||||
proc romanToDecimal*(romanVal: string): int =
|
||||
@@ -28,7 +28,7 @@ proc romanToDecimal*(romanVal: string): int =
|
||||
of 'C', 'c': val = 100
|
||||
of 'D', 'd': val = 500
|
||||
of 'M', 'm': val = 1000
|
||||
else:
|
||||
else:
|
||||
raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
|
||||
if val >= prevVal:
|
||||
inc(result, val)
|
||||
|
||||
@@ -29,7 +29,7 @@ const
|
||||
var
|
||||
cacheEnabled = false
|
||||
|
||||
type
|
||||
type
|
||||
Rope* = ref RopeObj ## empty rope is represented by nil
|
||||
RopeObj {.acyclic.} = object
|
||||
left, right: Rope
|
||||
@@ -59,7 +59,7 @@ proc newRope(data: string): Rope =
|
||||
result.length = len(data)
|
||||
result.data = data
|
||||
|
||||
var
|
||||
var
|
||||
cache {.threadvar.}: Rope # the root of the cache tree
|
||||
N {.threadvar.}: Rope # dummy rope needed for splay algorithm
|
||||
|
||||
@@ -81,7 +81,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope =
|
||||
t.left = y.right
|
||||
y.right = t
|
||||
t = y
|
||||
if t.left == nil: break
|
||||
if t.left == nil: break
|
||||
r.left = t
|
||||
r = t
|
||||
t = t.left
|
||||
@@ -91,7 +91,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope =
|
||||
t.right = y.left
|
||||
y.left = t
|
||||
t = y
|
||||
if t.right == nil: break
|
||||
if t.right == nil: break
|
||||
le.right = t
|
||||
le = t
|
||||
t = t.right
|
||||
@@ -109,7 +109,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
|
||||
if t == nil:
|
||||
result = newRope(s)
|
||||
when countCacheMisses: inc(misses)
|
||||
return
|
||||
return
|
||||
var cmp: int
|
||||
t = splay(s, t, cmp)
|
||||
if cmp == 0:
|
||||
@@ -197,7 +197,7 @@ proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} =
|
||||
proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} =
|
||||
## adds `b` to the rope `a`.
|
||||
a = a & b
|
||||
|
||||
|
||||
proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||
## returns the character at position `i` in the rope `r`. This is quite
|
||||
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
||||
@@ -250,7 +250,7 @@ when false:
|
||||
proc compiledArg(idx: int): Rope =
|
||||
new(result)
|
||||
result.length = -idx
|
||||
|
||||
|
||||
proc compileFrmt(frmt: string): Rope =
|
||||
var i = 0
|
||||
var length = len(frmt)
|
||||
@@ -272,7 +272,7 @@ when false:
|
||||
while true:
|
||||
j = j * 10 + ord(frmt[i]) - ord('0')
|
||||
inc(i)
|
||||
if frmt[i] notin {'0'..'9'}: break
|
||||
if frmt[i] notin {'0'..'9'}: break
|
||||
add(s, compiledArg(j))
|
||||
of '{':
|
||||
inc(i)
|
||||
@@ -345,7 +345,7 @@ const
|
||||
|
||||
proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} =
|
||||
## returns true if the contents of the file `f` equal `r`.
|
||||
var
|
||||
var
|
||||
buf: array[bufSize, char]
|
||||
bpos = buf.len
|
||||
blen = buf.len
|
||||
@@ -363,7 +363,7 @@ proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} =
|
||||
return
|
||||
let n = min(blen - bpos, slen - spos)
|
||||
# TODO There's gotta be a better way of comparing here...
|
||||
if not equalMem(addr(buf[bpos]),
|
||||
if not equalMem(addr(buf[bpos]),
|
||||
cast[pointer](cast[int](cstring(s))+spos), n):
|
||||
result = false
|
||||
return
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#
|
||||
|
||||
## This module implements helper procs for SCGI applications. Example:
|
||||
##
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## import strtabs, sockets, scgi
|
||||
##
|
||||
## var counter = 0
|
||||
## proc handleRequest(client: Socket, input: string,
|
||||
## proc handleRequest(client: Socket, input: string,
|
||||
## headers: StringTableRef): bool {.procvar.} =
|
||||
## inc(counter)
|
||||
## client.writeStatusOkTextContent()
|
||||
@@ -37,19 +37,19 @@ import sockets, strutils, os, strtabs, asyncio
|
||||
type
|
||||
ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
|
||||
|
||||
proc raiseScgiError*(msg: string) {.noreturn.} =
|
||||
proc raiseScgiError*(msg: string) {.noreturn.} =
|
||||
## raises an ScgiError exception with message `msg`.
|
||||
var e: ref ScgiError
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
||||
proc parseWord(inp: string, outp: var string, start: int): int =
|
||||
proc parseWord(inp: string, outp: var string, start: int): int =
|
||||
result = start
|
||||
while inp[result] != '\0': inc(result)
|
||||
outp = substr(inp, start, result-1)
|
||||
|
||||
proc parseHeaders(s: string, L: int): StringTableRef =
|
||||
proc parseHeaders(s: string, L: int): StringTableRef =
|
||||
result = newStringTable()
|
||||
var i = 0
|
||||
while i < L:
|
||||
@@ -59,12 +59,12 @@ proc parseHeaders(s: string, L: int): StringTableRef =
|
||||
result[key] = val
|
||||
if s[i] == ',': inc(i)
|
||||
else: raiseScgiError("',' after netstring expected")
|
||||
|
||||
proc recvChar(s: Socket): char =
|
||||
|
||||
proc recvChar(s: Socket): char =
|
||||
var c: char
|
||||
if recv(s, addr(c), sizeof(c)) == sizeof(c):
|
||||
if recv(s, addr(c), sizeof(c)) == sizeof(c):
|
||||
result = c
|
||||
|
||||
|
||||
type
|
||||
ScgiState* = object of RootObj ## SCGI state object
|
||||
server: Socket
|
||||
@@ -72,22 +72,22 @@ type
|
||||
client*: Socket ## the client socket to send data to
|
||||
headers*: StringTableRef ## the parsed headers
|
||||
input*: string ## the input buffer
|
||||
|
||||
|
||||
|
||||
|
||||
# Async
|
||||
|
||||
|
||||
ClientMode = enum
|
||||
ClientReadChar, ClientReadHeaders, ClientReadContent
|
||||
|
||||
|
||||
AsyncClient = ref object
|
||||
c: AsyncSocket
|
||||
mode: ClientMode
|
||||
dataLen: int
|
||||
headers: StringTableRef ## the parsed headers
|
||||
input: string ## the input buffer
|
||||
|
||||
|
||||
AsyncScgiStateObj = object
|
||||
handleRequest: proc (client: AsyncSocket,
|
||||
handleRequest: proc (client: AsyncSocket,
|
||||
input: string,
|
||||
headers: StringTableRef) {.closure, gcsafe.}
|
||||
asyncServer: AsyncSocket
|
||||
@@ -98,19 +98,19 @@ type
|
||||
PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
|
||||
|
||||
proc recvBuffer(s: var ScgiState, L: int) =
|
||||
if L > s.bufLen:
|
||||
if L > s.bufLen:
|
||||
s.bufLen = L
|
||||
s.input = newString(L)
|
||||
if L > 0 and recv(s.client, cstring(s.input), L) != L:
|
||||
if L > 0 and recv(s.client, cstring(s.input), L) != L:
|
||||
raiseScgiError("could not read all data")
|
||||
setLen(s.input, L)
|
||||
|
||||
|
||||
proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
|
||||
reuseAddr = false) =
|
||||
reuseAddr = false) =
|
||||
## opens a connection.
|
||||
s.bufLen = 4000
|
||||
s.input = newString(s.bufLen) # will be reused
|
||||
|
||||
|
||||
s.server = socket()
|
||||
if s.server == invalidSocket: raiseOSError(osLastError())
|
||||
new(s.client) # Initialise s.client for `next`
|
||||
@@ -120,12 +120,12 @@ proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
|
||||
s.server.setSockOpt(OptReuseAddr, true)
|
||||
bindAddr(s.server, port, address)
|
||||
listen(s.server)
|
||||
|
||||
proc close*(s: var ScgiState) =
|
||||
|
||||
proc close*(s: var ScgiState) =
|
||||
## closes the connection.
|
||||
s.server.close()
|
||||
|
||||
proc next*(s: var ScgiState, timeout: int = -1): bool =
|
||||
proc next*(s: var ScgiState, timeout: int = -1): bool =
|
||||
## proceed to the first/next request. Waits ``timeout`` milliseconds for a
|
||||
## request, if ``timeout`` is `-1` then this function will never time out.
|
||||
## Returns `true` if a new request has been processed.
|
||||
@@ -139,18 +139,18 @@ proc next*(s: var ScgiState, timeout: int = -1): bool =
|
||||
if d == '\0':
|
||||
s.client.close()
|
||||
return false
|
||||
if d notin strutils.Digits:
|
||||
if d notin strutils.Digits:
|
||||
if d != ':': raiseScgiError("':' after length expected")
|
||||
break
|
||||
L = L * 10 + ord(d) - ord('0')
|
||||
L = L * 10 + ord(d) - ord('0')
|
||||
recvBuffer(s, L+1)
|
||||
s.headers = parseHeaders(s.input, L)
|
||||
if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
|
||||
L = parseInt(s.headers["CONTENT_LENGTH"])
|
||||
recvBuffer(s, L)
|
||||
return true
|
||||
|
||||
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
|
||||
|
||||
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
|
||||
## sends the following string to the socket `c`::
|
||||
##
|
||||
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
|
||||
@@ -159,9 +159,9 @@ proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
|
||||
c.send("Status: 200 OK\r\L" &
|
||||
"Content-Type: $1\r\L\r\L" % contentType)
|
||||
|
||||
proc run*(handleRequest: proc (client: Socket, input: string,
|
||||
proc run*(handleRequest: proc (client: Socket, input: string,
|
||||
headers: StringTableRef): bool {.nimcall,gcsafe.},
|
||||
port = Port(4000)) =
|
||||
port = Port(4000)) =
|
||||
## encapsulates the SCGI object and main loop.
|
||||
var s: ScgiState
|
||||
s.open(port)
|
||||
@@ -197,7 +197,7 @@ proc checkCloseSocket(client: AsyncClient) =
|
||||
s.close()
|
||||
s.delHandleWrite()
|
||||
else: client.c.close()
|
||||
|
||||
|
||||
proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
|
||||
case client.mode
|
||||
of ClientReadChar:
|
||||
@@ -223,7 +223,7 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
|
||||
client.headers = parseHeaders(client.input, client.input.len-1)
|
||||
if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
|
||||
client.input = "" # For next part
|
||||
|
||||
|
||||
let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
|
||||
if contentLen > 0:
|
||||
client.mode = ClientReadContent
|
||||
@@ -250,12 +250,12 @@ proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
|
||||
accept(s.asyncServer, client)
|
||||
var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
|
||||
headers: newStringTable(), input: "")
|
||||
client.handleRead =
|
||||
client.handleRead =
|
||||
proc (sock: AsyncSocket) =
|
||||
handleClientRead(asyncClient, s)
|
||||
s.disp.register(client)
|
||||
|
||||
proc open*(handleRequest: proc (client: AsyncSocket,
|
||||
proc open*(handleRequest: proc (client: AsyncSocket,
|
||||
input: string, headers: StringTableRef) {.
|
||||
closure, gcsafe.},
|
||||
port = Port(4000), address = "127.0.0.1",
|
||||
@@ -286,7 +286,7 @@ proc close*(s: AsyncScgiState) =
|
||||
|
||||
when false:
|
||||
var counter = 0
|
||||
proc handleRequest(client: Socket, input: string,
|
||||
proc handleRequest(client: Socket, input: string,
|
||||
headers: StringTableRef): bool {.procvar.} =
|
||||
inc(counter)
|
||||
client.writeStatusOkTextContent()
|
||||
|
||||
@@ -7,25 +7,25 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the SMTP client protocol as specified by RFC 5321,
|
||||
## This module implements the SMTP client protocol as specified by RFC 5321,
|
||||
## this can be used to send mail to any SMTP Server.
|
||||
##
|
||||
## This module also implements the protocol used to format messages,
|
||||
##
|
||||
## This module also implements the protocol used to format messages,
|
||||
## as specified by RFC 2822.
|
||||
##
|
||||
##
|
||||
## Example gmail use:
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var msg = createMessage("Hello from Nim's SMTP",
|
||||
## "Hello!.\n Is this awesome or what?",
|
||||
## var msg = createMessage("Hello from Nim's SMTP",
|
||||
## "Hello!.\n Is this awesome or what?",
|
||||
## @["foo@gmail.com"])
|
||||
## var smtp = connect("smtp.gmail.com", 465, true, true)
|
||||
## smtp.auth("username", "password")
|
||||
## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
||||
##
|
||||
##
|
||||
## For SSL support this module relies on OpenSSL. If you want to
|
||||
##
|
||||
##
|
||||
## For SSL support this module relies on OpenSSL. If you want to
|
||||
## enable SSL, compile with ``-d:ssl``.
|
||||
|
||||
import net, strutils, strtabs, base64, os
|
||||
@@ -35,14 +35,14 @@ type
|
||||
Smtp* = object
|
||||
sock: Socket
|
||||
debug: bool
|
||||
|
||||
|
||||
Message* = object
|
||||
msgTo: seq[string]
|
||||
msgCc: seq[string]
|
||||
msgSubject: string
|
||||
msgOtherHeaders: StringTableRef
|
||||
msgBody: string
|
||||
|
||||
|
||||
ReplyError* = object of IOError
|
||||
|
||||
AsyncSmtp* = ref object
|
||||
@@ -84,7 +84,7 @@ when not defined(ssl):
|
||||
else:
|
||||
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
||||
|
||||
proc connect*(address: string, port = Port(25),
|
||||
proc connect*(address: string, port = Port(25),
|
||||
ssl = false, debug = false,
|
||||
sslContext = defaultSSLContext): Smtp =
|
||||
## Establishes a connection with a SMTP server.
|
||||
@@ -94,17 +94,17 @@ proc connect*(address: string, port = Port(25),
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(ESystem,
|
||||
raise newException(ESystem,
|
||||
"SMTP module compiled without SSL support")
|
||||
result.sock.connect(address, port)
|
||||
result.debug = debug
|
||||
|
||||
|
||||
result.checkReply("220")
|
||||
result.debugSend("HELO " & address & "\c\L")
|
||||
result.checkReply("250")
|
||||
|
||||
proc auth*(smtp: var Smtp, username, password: string) =
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## using `password`.
|
||||
## May fail with ReplyError.
|
||||
|
||||
@@ -113,13 +113,13 @@ proc auth*(smtp: var Smtp, username, password: string) =
|
||||
# i.e "334 VXNlcm5hbWU6"
|
||||
smtp.debugSend(encode(username) & "\c\L")
|
||||
smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||
|
||||
|
||||
smtp.debugSend(encode(password) & "\c\L")
|
||||
smtp.checkReply("235") # Check whether the authentification was successful.
|
||||
|
||||
proc sendmail*(smtp: var Smtp, fromaddr: string,
|
||||
toaddrs: seq[string], msg: string) =
|
||||
## Sends `msg` from `fromaddr` to `toaddr`.
|
||||
## Sends `msg` from `fromaddr` to `toaddr`.
|
||||
## Messages may be formed using ``createMessage`` by converting the
|
||||
## Message into a string.
|
||||
|
||||
@@ -128,7 +128,7 @@ proc sendmail*(smtp: var Smtp, fromaddr: string,
|
||||
for address in items(toaddrs):
|
||||
smtp.debugSend("RCPT TO:<" & address & ">\c\L")
|
||||
smtp.checkReply("250")
|
||||
|
||||
|
||||
# Send the message
|
||||
smtp.debugSend("DATA " & "\c\L")
|
||||
smtp.checkReply("354")
|
||||
@@ -175,7 +175,7 @@ proc `$`*(msg: Message): string =
|
||||
|
||||
result.add("\c\L")
|
||||
result.add(msg.msgBody)
|
||||
|
||||
|
||||
proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
|
||||
sslContext = defaultSslContext): AsyncSmtp =
|
||||
## Creates a new ``AsyncSmtp`` instance.
|
||||
@@ -189,7 +189,7 @@ proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(ESystem,
|
||||
raise newException(ESystem,
|
||||
"SMTP module compiled without SSL support")
|
||||
|
||||
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
|
||||
@@ -216,7 +216,7 @@ proc connect*(smtp: AsyncSmtp) {.async.} =
|
||||
await smtp.checkReply("250")
|
||||
|
||||
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## using `password`.
|
||||
## May fail with ReplyError.
|
||||
|
||||
@@ -225,7 +225,7 @@ proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
|
||||
# i.e "334 VXNlcm5hbWU6"
|
||||
await smtp.sock.send(encode(username) & "\c\L")
|
||||
await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||
|
||||
|
||||
await smtp.sock.send(encode(password) & "\c\L")
|
||||
await smtp.checkReply("235") # Check whether the authentification was successful.
|
||||
|
||||
@@ -240,7 +240,7 @@ proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
|
||||
for address in items(toAddrs):
|
||||
await smtp.sock.send("RCPT TO:<" & address & ">\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
|
||||
# Send the message
|
||||
await smtp.sock.send("DATA " & "\c\L")
|
||||
await smtp.checkReply("354")
|
||||
@@ -254,24 +254,24 @@ proc close*(smtp: AsyncSmtp) {.async.} =
|
||||
smtp.sock.close()
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
#var msg = createMessage("Test subject!",
|
||||
#var msg = createMessage("Test subject!",
|
||||
# "Hello, my name is dom96.\n What\'s yours?", @["dominik@localhost"])
|
||||
#echo(msg)
|
||||
|
||||
#var smtp = connect("localhost", 25, False, True)
|
||||
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
|
||||
|
||||
|
||||
#echo(decode("a17sm3701420wbe.12"))
|
||||
proc main() {.async.} =
|
||||
var client = newAsyncSmtp("smtp.gmail.com", Port(465), true)
|
||||
await client.connect()
|
||||
await client.auth("johndoe", "foo")
|
||||
var msg = createMessage("Hello from Nim's SMTP!",
|
||||
"Hello!!!!.\n Is this awesome or what?",
|
||||
var msg = createMessage("Hello from Nim's SMTP!",
|
||||
"Hello!!!!.\n Is this awesome or what?",
|
||||
@["blah@gmail.com"])
|
||||
echo(msg)
|
||||
await client.sendMail("blah@gmail.com", @["blah@gmail.com"], $msg)
|
||||
|
||||
await client.close()
|
||||
|
||||
|
||||
waitFor main()
|
||||
|
||||
@@ -51,17 +51,17 @@ else:
|
||||
|
||||
# Note: The enumerations are mapped to Window's constants.
|
||||
|
||||
when defined(ssl):
|
||||
when defined(ssl):
|
||||
|
||||
type
|
||||
SSLError* = object of Exception
|
||||
|
||||
SSLCVerifyMode* = enum
|
||||
CVerifyNone, CVerifyPeer
|
||||
|
||||
|
||||
SSLProtVersion* = enum
|
||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||
|
||||
|
||||
SSLContext* = distinct SSLCTX
|
||||
|
||||
SSLAcceptResult* = enum
|
||||
@@ -93,11 +93,11 @@ type
|
||||
sslPeekChar: char
|
||||
of false: nil
|
||||
nonblocking: bool
|
||||
|
||||
|
||||
Socket* = ref SocketImpl
|
||||
|
||||
|
||||
Port* = distinct uint16 ## port type
|
||||
|
||||
|
||||
Domain* = enum ## domain, which specifies the protocol family of the
|
||||
## created socket. Other domains than those that are listed
|
||||
## here are unsupported.
|
||||
@@ -112,7 +112,7 @@ type
|
||||
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
||||
|
||||
Protocol* = enum ## third argument to `socket` proc
|
||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
|
||||
@@ -178,7 +178,7 @@ proc `==`*(a, b: Port): bool {.borrow.}
|
||||
proc `$`*(p: Port): string {.borrow.}
|
||||
## returns the port number as a string
|
||||
|
||||
proc ntohl*(x: int32): int32 =
|
||||
proc ntohl*(x: int32): int32 =
|
||||
## Converts 32-bit integers from network to host byte order.
|
||||
## On machines where the host byte order is the same as network byte order,
|
||||
## this is a no-op; otherwise, it performs a 4-byte swap operation.
|
||||
@@ -206,7 +206,7 @@ proc htons*(x: int16): int16 =
|
||||
## On machines where the host byte order is the same as network byte
|
||||
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
||||
result = sockets.ntohs(x)
|
||||
|
||||
|
||||
when defined(Posix):
|
||||
proc toInt(domain: Domain): cint =
|
||||
case domain
|
||||
@@ -234,19 +234,19 @@ when defined(Posix):
|
||||
else: discard
|
||||
|
||||
else:
|
||||
proc toInt(domain: Domain): cint =
|
||||
proc toInt(domain: Domain): cint =
|
||||
result = toU16(ord(domain))
|
||||
|
||||
proc toInt(typ: SockType): cint =
|
||||
result = cint(ord(typ))
|
||||
|
||||
|
||||
proc toInt(p: Protocol): cint =
|
||||
result = cint(ord(p))
|
||||
|
||||
proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
|
||||
|
||||
# TODO: Perhaps this should just raise EOS when an error occurs.
|
||||
when defined(Windows):
|
||||
result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
|
||||
@@ -277,27 +277,27 @@ when defined(ssl):
|
||||
raise newException(system.IOError, "Certificate file could not be found: " & certFile)
|
||||
if keyFile != "" and not existsFile(keyFile):
|
||||
raise newException(system.IOError, "Key file could not be found: " & keyFile)
|
||||
|
||||
|
||||
if certFile != "":
|
||||
var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
|
||||
if ret != 1:
|
||||
raiseSslError()
|
||||
|
||||
|
||||
# TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
|
||||
if keyFile != "":
|
||||
if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
|
||||
SSL_FILETYPE_PEM) != 1:
|
||||
raiseSslError()
|
||||
|
||||
|
||||
if SSL_CTX_check_private_key(ctx) != 1:
|
||||
raiseSslError("Verification of private key file failed.")
|
||||
|
||||
proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
|
||||
certFile = "", keyFile = ""): SSLContext =
|
||||
## Creates an SSL context.
|
||||
##
|
||||
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
|
||||
## are available with the addition of ``ProtSSLv23`` which allows for
|
||||
##
|
||||
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
|
||||
## are available with the addition of ``ProtSSLv23`` which allows for
|
||||
## compatibility with all of them.
|
||||
##
|
||||
## There are currently only two options for verify mode;
|
||||
@@ -322,7 +322,7 @@ when defined(ssl):
|
||||
newCTX = SSL_CTX_new(SSLv3_method())
|
||||
of protTLSv1:
|
||||
newCTX = SSL_CTX_new(TLSv1_method())
|
||||
|
||||
|
||||
if newCTX.SSLCTXSetCipherList("ALL") != 1:
|
||||
raiseSslError()
|
||||
case verifyMode
|
||||
@@ -343,7 +343,7 @@ when defined(ssl):
|
||||
##
|
||||
## **Disclaimer**: This code is not well tested, may be very unsafe and
|
||||
## prone to security vulnerabilities.
|
||||
|
||||
|
||||
socket.isSSL = true
|
||||
socket.sslContext = ctx
|
||||
socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
|
||||
@@ -351,7 +351,7 @@ when defined(ssl):
|
||||
socket.sslHasPeekChar = false
|
||||
if socket.sslHandle == nil:
|
||||
raiseSslError()
|
||||
|
||||
|
||||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||
raiseSslError()
|
||||
|
||||
@@ -382,7 +382,7 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
|
||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||
raiseSslError()
|
||||
else: raiseSslError("Unknown Error")
|
||||
|
||||
|
||||
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||
let lastError = osLastError()
|
||||
if async:
|
||||
@@ -397,15 +397,15 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
|
||||
else: raiseOSError(lastError)
|
||||
|
||||
proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
|
||||
|
||||
proc invalidIp4(s: string) {.noreturn, noinline.} =
|
||||
raise newException(ValueError, "invalid ip4 address: " & s)
|
||||
|
||||
proc parseIp4*(s: string): BiggestInt =
|
||||
proc parseIp4*(s: string): BiggestInt =
|
||||
## parses an IP version 4 in dotted decimal form like "a.b.c.d".
|
||||
##
|
||||
## This is equivalent to `inet_ntoa`:idx:.
|
||||
@@ -469,8 +469,8 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
|
||||
gaiNim(address, port, hints, aiList)
|
||||
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc getSockName*(socket: Socket): Port =
|
||||
|
||||
proc getSockName*(socket: Socket): Port =
|
||||
## returns the socket's associated port number.
|
||||
var name: Sockaddr_in
|
||||
when defined(Windows):
|
||||
@@ -485,14 +485,14 @@ proc getSockName*(socket: Socket): Port =
|
||||
raiseOSError(osLastError())
|
||||
result = Port(sockets.ntohs(name.sin_port))
|
||||
|
||||
template acceptAddrPlain(noClientRet, successRet: expr,
|
||||
template acceptAddrPlain(noClientRet, successRet: expr,
|
||||
sslImplementation: stmt): stmt {.immediate.} =
|
||||
assert(client != nil)
|
||||
var sockAddress: Sockaddr_in
|
||||
var addrLen = sizeof(sockAddress).SockLen
|
||||
var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
|
||||
addr(addrLen))
|
||||
|
||||
|
||||
if sock == osInvalidSocket:
|
||||
let err = osLastError()
|
||||
when defined(windows):
|
||||
@@ -537,7 +537,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
|
||||
## The resulting client will inherit any properties of the server socket. For
|
||||
## example: whether the socket is buffered or not.
|
||||
##
|
||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||
## makes no effort to initialise the ``client`` variable.
|
||||
##
|
||||
## **Warning:** When using SSL with non-blocking sockets, it is best to use
|
||||
@@ -546,7 +546,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
|
||||
when defined(ssl):
|
||||
if server.isSSL:
|
||||
# We must wrap the client sock in a ssl context.
|
||||
|
||||
|
||||
server.sslContext.wrapSocket(client)
|
||||
let ret = SSLAccept(client.sslHandle)
|
||||
while ret <= 0:
|
||||
@@ -572,9 +572,9 @@ when defined(ssl):
|
||||
proc acceptAddrSSL*(server: Socket, client: var Socket,
|
||||
address: var string): SSLAcceptResult {.
|
||||
tags: [ReadIOEffect].} =
|
||||
## This procedure should only be used for non-blocking **SSL** sockets.
|
||||
## This procedure should only be used for non-blocking **SSL** sockets.
|
||||
## It will immediately return with one of the following values:
|
||||
##
|
||||
##
|
||||
## ``AcceptSuccess`` will be returned when a client has been successfully
|
||||
## accepted and the handshake has been successfully performed between
|
||||
## ``server`` and the newly connected client.
|
||||
@@ -591,7 +591,7 @@ when defined(ssl):
|
||||
if server.isSSL:
|
||||
client.setBlocking(false)
|
||||
# We must wrap the client sock in a ssl context.
|
||||
|
||||
|
||||
if not client.isSSL or client.sslHandle == nil:
|
||||
server.sslContext.wrapSocket(client)
|
||||
let ret = SSLAccept(client.sslHandle)
|
||||
@@ -623,10 +623,10 @@ when defined(ssl):
|
||||
proc accept*(server: Socket, client: var Socket) {.tags: [ReadIOEffect].} =
|
||||
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
|
||||
## socket.
|
||||
##
|
||||
##
|
||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||
## makes no effort to initialise the ``client`` variable.
|
||||
|
||||
|
||||
var addrDummy = ""
|
||||
acceptAddr(server, client, addrDummy)
|
||||
|
||||
@@ -662,7 +662,7 @@ proc close*(socket: Socket) =
|
||||
socket.sslHandle = nil
|
||||
|
||||
proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the service name specified by ``name`` matches the s_name member
|
||||
## and the protocol name specified by ``proto`` matches the s_proto member.
|
||||
##
|
||||
@@ -676,10 +676,10 @@ proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
|
||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||
result.port = Port(s.s_port)
|
||||
result.proto = $s.s_proto
|
||||
|
||||
proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the port specified by ``port`` matches the s_port member and the
|
||||
|
||||
proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the port specified by ``port`` matches the s_port member and the
|
||||
## protocol name specified by ``proto`` matches the s_proto member.
|
||||
##
|
||||
## On posix this will search through the ``/etc/services`` file.
|
||||
@@ -697,20 +697,20 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
|
||||
## This function will lookup the hostname of an IP Address.
|
||||
var myaddr: InAddr
|
||||
myaddr.s_addr = inet_addr(ip)
|
||||
|
||||
|
||||
when defined(windows):
|
||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||
cint(sockets.AF_INET))
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
else:
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
|
||||
cint(posix.AF_INET))
|
||||
if s == nil:
|
||||
raiseOSError(osLastError(), $hstrerror(h_errno))
|
||||
|
||||
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when defined(windows):
|
||||
when defined(windows):
|
||||
result.addrtype = Domain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
@@ -722,7 +722,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
|
||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||
result.length = int(s.h_length)
|
||||
|
||||
proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
|
||||
proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
|
||||
## This function will lookup the IP address of a hostname.
|
||||
when defined(Windows):
|
||||
var s = winlean.gethostbyname(name)
|
||||
@@ -731,7 +731,7 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when defined(windows):
|
||||
when defined(windows):
|
||||
result.addrtype = Domain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
@@ -744,11 +744,11 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
|
||||
result.length = int(s.h_length)
|
||||
|
||||
proc getSockOptInt*(socket: Socket, level, optname: int): int {.
|
||||
tags: [ReadIOEffect].} =
|
||||
tags: [ReadIOEffect].} =
|
||||
## getsockopt for integer options.
|
||||
var res: cint
|
||||
var size = sizeof(res).SockLen
|
||||
if getsockopt(socket.fd, cint(level), cint(optname),
|
||||
if getsockopt(socket.fd, cint(level), cint(optname),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
result = int(res)
|
||||
@@ -757,7 +757,7 @@ proc setSockOptInt*(socket: Socket, level, optname, optval: int) {.
|
||||
tags: [WriteIOEffect].} =
|
||||
## setsockopt for integer options.
|
||||
var value = cint(optval)
|
||||
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
|
||||
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
|
||||
sizeof(value).SockLen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
@@ -776,7 +776,7 @@ proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
|
||||
## Retrieves option ``opt`` as a boolean value.
|
||||
var res: cint
|
||||
var size = sizeof(res).SockLen
|
||||
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
||||
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
result = res != 0
|
||||
@@ -785,11 +785,11 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
|
||||
tags: [WriteIOEffect].} =
|
||||
## Sets option ``opt`` to a boolean value specified by ``value``.
|
||||
var valuei = cint(if value: 1 else: 0)
|
||||
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
||||
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
||||
sizeof(valuei).SockLen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
|
||||
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
|
||||
## host name. If ``address`` is a host name, this function will try each IP
|
||||
@@ -816,7 +816,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: raiseOSError(lastError)
|
||||
|
||||
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
let ret = SSLConnect(socket.sslHandle)
|
||||
@@ -825,7 +825,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
case err
|
||||
of SSL_ERROR_ZERO_RETURN:
|
||||
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
|
||||
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
|
||||
SSL_ERROR_WANT_ACCEPT:
|
||||
raiseSslError("The operation did not complete. Perhaps you should use connectAsync?")
|
||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||
@@ -834,7 +834,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
raiseSslError()
|
||||
else:
|
||||
raiseSslError("Unknown error")
|
||||
|
||||
|
||||
when false:
|
||||
var s: TSockAddrIn
|
||||
s.sin_addr.s_addr = inet_addr(address)
|
||||
@@ -842,7 +842,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
when defined(windows):
|
||||
s.sin_family = toU16(ord(af))
|
||||
else:
|
||||
case af
|
||||
case af
|
||||
of AF_UNIX: s.sin_family = posix.AF_UNIX
|
||||
of AF_INET: s.sin_family = posix.AF_INET
|
||||
of AF_INET6: s.sin_family = posix.AF_INET6
|
||||
@@ -886,7 +886,7 @@ proc connectAsync*(socket: Socket, name: string, port = Port(0),
|
||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||
success = true
|
||||
break
|
||||
|
||||
|
||||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
@@ -942,12 +942,12 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
|
||||
result.tv_sec = seconds.int32
|
||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||
|
||||
proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
|
||||
proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
|
||||
FD_ZERO(fd)
|
||||
for i in items(s):
|
||||
for i in items(s):
|
||||
m = max(m, int(i.fd))
|
||||
FD_SET(i.fd, fd)
|
||||
|
||||
|
||||
proc pruneSocketSet(s: var seq[Socket], fd: var TFdSet) =
|
||||
var i = 0
|
||||
var L = s.len
|
||||
@@ -982,13 +982,13 @@ proc checkBuffer(readfds: var seq[Socket]): int =
|
||||
if result > 0:
|
||||
readfds = res
|
||||
|
||||
proc select*(readfds, writefds, exceptfds: var seq[Socket],
|
||||
timeout = 500): int {.tags: [ReadIOEffect].} =
|
||||
proc select*(readfds, writefds, exceptfds: var seq[Socket],
|
||||
timeout = 500): int {.tags: [ReadIOEffect].} =
|
||||
## Traditional select function. This function will return the number of
|
||||
## sockets that are ready to be read from, written to, or which have errors.
|
||||
## If there are none; 0 is returned.
|
||||
## If there are none; 0 is returned.
|
||||
## ``Timeout`` is in milliseconds and -1 can be specified for no timeout.
|
||||
##
|
||||
##
|
||||
## Sockets which are **not** ready for reading, writing or which don't have
|
||||
## errors waiting on them are removed from the ``readfds``, ``writefds``,
|
||||
## ``exceptfds`` sequences respectively.
|
||||
@@ -997,44 +997,44 @@ proc select*(readfds, writefds, exceptfds: var seq[Socket],
|
||||
return buffersFilled
|
||||
|
||||
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
|
||||
var rd, wr, ex: TFdSet
|
||||
var m = 0
|
||||
createFdSet((rd), readfds, m)
|
||||
createFdSet((wr), writefds, m)
|
||||
createFdSet((ex), exceptfds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
|
||||
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
pruneSocketSet(writefds, (wr))
|
||||
pruneSocketSet(exceptfds, (ex))
|
||||
|
||||
proc select*(readfds, writefds: var seq[Socket],
|
||||
proc select*(readfds, writefds: var seq[Socket],
|
||||
timeout = 500): int {.tags: [ReadIOEffect].} =
|
||||
## Variant of select with only a read and write list.
|
||||
let buffersFilled = checkBuffer(readfds)
|
||||
if buffersFilled > 0:
|
||||
return buffersFilled
|
||||
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
|
||||
var rd, wr: TFdSet
|
||||
var m = 0
|
||||
createFdSet((rd), readfds, m)
|
||||
createFdSet((wr), writefds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
||||
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
pruneSocketSet(writefds, (wr))
|
||||
|
||||
proc selectWrite*(writefds: var seq[Socket],
|
||||
proc selectWrite*(writefds: var seq[Socket],
|
||||
timeout = 500): int {.tags: [ReadIOEffect].} =
|
||||
## When a socket in ``writefds`` is ready to be written to then a non-zero
|
||||
## value will be returned specifying the count of the sockets which can be
|
||||
@@ -1044,16 +1044,16 @@ proc selectWrite*(writefds: var seq[Socket],
|
||||
## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
|
||||
## an unlimited time.
|
||||
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
|
||||
var wr: TFdSet
|
||||
var m = 0
|
||||
createFdSet((wr), writefds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), nil, addr(wr), nil, nil))
|
||||
|
||||
|
||||
pruneSocketSet(writefds, (wr))
|
||||
|
||||
proc select*(readfds: var seq[Socket], timeout = 500): int =
|
||||
@@ -1062,16 +1062,16 @@ proc select*(readfds: var seq[Socket], timeout = 500): int =
|
||||
if buffersFilled > 0:
|
||||
return buffersFilled
|
||||
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
|
||||
var rd: TFdSet
|
||||
var m = 0
|
||||
createFdSet((rd), readfds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
|
||||
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
|
||||
proc readIntoBuf(socket: Socket, flags: int32): int =
|
||||
@@ -1107,12 +1107,12 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
|
||||
if socket.isBuffered:
|
||||
if socket.bufLen == 0:
|
||||
retRead(0'i32, 0)
|
||||
|
||||
|
||||
var read = 0
|
||||
while read < size:
|
||||
if socket.currPos >= socket.bufLen:
|
||||
retRead(0'i32, read)
|
||||
|
||||
|
||||
let chunk = min(socket.bufLen-socket.currPos, size-read)
|
||||
var d = cast[cstring](data)
|
||||
copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
|
||||
@@ -1155,7 +1155,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||
else:
|
||||
if timeout - int(waited * 1000.0) < 1:
|
||||
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||
|
||||
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
if socket.hasDataBuffered:
|
||||
@@ -1164,7 +1164,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||
let sslPending = SSLPending(socket.sslHandle)
|
||||
if sslPending != 0:
|
||||
return sslPending
|
||||
|
||||
|
||||
var s = @[socket]
|
||||
var startTime = epochTime()
|
||||
let selRet = select(s, timeout - int(waited * 1000.0))
|
||||
@@ -1176,8 +1176,8 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||
proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
|
||||
tags: [ReadIOEffect, TimeEffect].} =
|
||||
## overload with a ``timeout`` parameter in milliseconds.
|
||||
var waited = 0.0 # number of seconds already waited
|
||||
|
||||
var waited = 0.0 # number of seconds already waited
|
||||
|
||||
var read = 0
|
||||
while read < size:
|
||||
let avail = waitFor(socket, waited, timeout, size-read, "recv")
|
||||
@@ -1187,7 +1187,7 @@ proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
|
||||
if result < 0:
|
||||
return result
|
||||
inc(read, result)
|
||||
|
||||
|
||||
result = read
|
||||
|
||||
proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int =
|
||||
@@ -1231,7 +1231,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
|
||||
var res = socket.readIntoBuf(0'i32)
|
||||
if res <= 0:
|
||||
result = res
|
||||
|
||||
|
||||
c = socket.buffer[socket.currPos]
|
||||
else:
|
||||
when defined(ssl):
|
||||
@@ -1239,7 +1239,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
|
||||
if not socket.sslHasPeekChar:
|
||||
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
|
||||
socket.sslHasPeekChar = true
|
||||
|
||||
|
||||
c = socket.sslPeekChar
|
||||
return
|
||||
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
|
||||
@@ -1251,11 +1251,11 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
|
||||
## If a full line is received ``\r\L`` is not
|
||||
## added to ``line``, however if solely ``\r\L`` is received then ``line``
|
||||
## will be set to it.
|
||||
##
|
||||
##
|
||||
## ``True`` is returned if data is available. ``False`` suggests an
|
||||
## error, EOS exceptions are not raised and ``False`` is simply returned
|
||||
## instead.
|
||||
##
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
|
||||
## will be returned.
|
||||
##
|
||||
@@ -1264,7 +1264,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
|
||||
##
|
||||
## **Deprecated since version 0.9.2**: This function has been deprecated in
|
||||
## favour of readLine.
|
||||
|
||||
|
||||
template addNLIfEmpty(): stmt =
|
||||
if line.len == 0:
|
||||
line.add("\c\L")
|
||||
@@ -1286,7 +1286,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
|
||||
elif n <= 0: return false
|
||||
addNLIfEmpty()
|
||||
return true
|
||||
elif c == '\L':
|
||||
elif c == '\L':
|
||||
addNLIfEmpty()
|
||||
return true
|
||||
add(line.string, c)
|
||||
@@ -1298,14 +1298,14 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
|
||||
## If a full line is read ``\r\L`` is not
|
||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||
## will be set to it.
|
||||
##
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||
##
|
||||
## An EOS exception will be raised in the case of a socket error.
|
||||
##
|
||||
## A timeout can be specified in milliseconds, if data is not received within
|
||||
## the specified time an ETimeout exception will be raised.
|
||||
|
||||
|
||||
template addNLIfEmpty(): stmt =
|
||||
if line.len == 0:
|
||||
line.add("\c\L")
|
||||
@@ -1327,12 +1327,12 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
|
||||
elif n <= 0: socket.raiseSocketError()
|
||||
addNLIfEmpty()
|
||||
return
|
||||
elif c == '\L':
|
||||
elif c == '\L':
|
||||
addNLIfEmpty()
|
||||
return
|
||||
add(line.string, c)
|
||||
|
||||
proc recvLineAsync*(socket: Socket,
|
||||
proc recvLineAsync*(socket: Socket,
|
||||
line: var TaintedString): RecvLineResult {.tags: [ReadIOEffect], deprecated.} =
|
||||
## Similar to ``recvLine`` but designed for non-blocking sockets.
|
||||
##
|
||||
@@ -1350,21 +1350,21 @@ proc recvLineAsync*(socket: Socket,
|
||||
while true:
|
||||
var c: char
|
||||
var n = recv(socket, addr(c), 1)
|
||||
if n < 0:
|
||||
if n < 0:
|
||||
return (if line.len == 0: RecvFail else: RecvPartialLine)
|
||||
elif n == 0:
|
||||
elif n == 0:
|
||||
return (if line.len == 0: RecvDisconnected else: RecvPartialLine)
|
||||
if c == '\r':
|
||||
n = peekChar(socket, c)
|
||||
if n > 0 and c == '\L':
|
||||
discard recv(socket, addr(c), 1)
|
||||
elif n <= 0:
|
||||
elif n <= 0:
|
||||
return (if line.len == 0: RecvFail else: RecvPartialLine)
|
||||
return RecvFullLine
|
||||
elif c == '\L': return RecvFullLine
|
||||
add(line.string, c)
|
||||
|
||||
proc readLineAsync*(socket: Socket,
|
||||
proc readLineAsync*(socket: Socket,
|
||||
line: var TaintedString): ReadLineResult {.tags: [ReadIOEffect].} =
|
||||
## Similar to ``recvLine`` but designed for non-blocking sockets.
|
||||
##
|
||||
@@ -1376,24 +1376,24 @@ proc readLineAsync*(socket: Socket,
|
||||
## * If no data could be retrieved; ``ReadNone`` is returned.
|
||||
## * If call to ``recv`` failed; **an EOS exception is raised.**
|
||||
setLen(line.string, 0)
|
||||
|
||||
|
||||
template errorOrNone =
|
||||
socket.raiseSocketError(async = true)
|
||||
return ReadNone
|
||||
|
||||
|
||||
while true:
|
||||
var c: char
|
||||
var n = recv(socket, addr(c), 1)
|
||||
#echo(n)
|
||||
if n < 0:
|
||||
if line.len == 0: errorOrNone else: return ReadPartialLine
|
||||
elif n == 0:
|
||||
elif n == 0:
|
||||
return (if line.len == 0: ReadDisconnected else: ReadPartialLine)
|
||||
if c == '\r':
|
||||
n = peekChar(socket, c)
|
||||
if n > 0 and c == '\L':
|
||||
discard recv(socket, addr(c), 1)
|
||||
elif n <= 0:
|
||||
elif n <= 0:
|
||||
if line.len == 0: errorOrNone else: return ReadPartialLine
|
||||
return ReadFullLine
|
||||
elif c == '\L': return ReadFullLine
|
||||
@@ -1424,7 +1424,7 @@ proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} =
|
||||
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
||||
# Error
|
||||
if bytesRead == -1: OSError(osLastError())
|
||||
|
||||
|
||||
buf[bytesRead] = '\0' # might not be necessary
|
||||
setLen(buf, bytesRead)
|
||||
add(result.string, buf)
|
||||
@@ -1442,13 +1442,13 @@ proc recvTimeout*(socket: Socket, timeout: int): TaintedString {.
|
||||
var s = @[socket]
|
||||
if s.select(timeout) != 1:
|
||||
raise newException(TimeoutError, "Call to recv() timed out.")
|
||||
|
||||
|
||||
return socket.recv
|
||||
{.pop.}
|
||||
|
||||
proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
|
||||
tags: [ReadIOEffect], deprecated.} =
|
||||
## receives all the data from a non-blocking socket. If socket is non-blocking
|
||||
## receives all the data from a non-blocking socket. If socket is non-blocking
|
||||
## and there are no messages available, `False` will be returned.
|
||||
## Other socket errors will result in an ``EOS`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
@@ -1478,7 +1478,7 @@ proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
|
||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||
raiseSslError()
|
||||
else: raiseSslError("Unknown Error")
|
||||
|
||||
|
||||
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||
let err = osLastError()
|
||||
when defined(windows):
|
||||
@@ -1510,7 +1510,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
|
||||
## so when ``socket`` is buffered the non-buffered implementation will be
|
||||
## used. Therefore if ``socket`` contains something in its buffer this
|
||||
## function will make no effort to return it.
|
||||
|
||||
|
||||
# TODO: Buffered sockets
|
||||
data.setLen(length)
|
||||
var sockAddress: Sockaddr_in
|
||||
@@ -1524,7 +1524,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
|
||||
port = ntohs(sockAddress.sin_port).Port
|
||||
|
||||
proc recvFromAsync*(socket: Socket, data: var string, length: int,
|
||||
address: var string, port: var Port,
|
||||
address: var string, port: var Port,
|
||||
flags = 0'i32): bool {.tags: [ReadIOEffect].} =
|
||||
## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``,
|
||||
## this function will raise an EOS error whenever a socket error occurs.
|
||||
@@ -1573,11 +1573,11 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
return SSLWrite(socket.sslHandle, cast[cstring](data), size)
|
||||
|
||||
|
||||
when defined(windows) or defined(macosx):
|
||||
result = send(socket.fd, data, size.cint, 0'i32)
|
||||
else:
|
||||
when defined(solaris):
|
||||
when defined(solaris):
|
||||
const MSG_NOSIGNAL = 0
|
||||
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
|
||||
|
||||
@@ -1590,7 +1590,7 @@ proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} =
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
raiseSslError()
|
||||
|
||||
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if sent != data.len:
|
||||
@@ -1633,7 +1633,7 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return 0
|
||||
else: raiseOSError(err)
|
||||
|
||||
|
||||
|
||||
proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
|
||||
## safe alternative to ``send``. Does not raise an EOS when an error occurs,
|
||||
@@ -1644,7 +1644,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||
size: int, af: Domain = AF_INET, flags = 0'i32): int {.
|
||||
tags: [WriteIOEffect].} =
|
||||
## low-level sendTo proc. This proc sends ``data`` to the specified ``address``,
|
||||
## which may be an IP address or a hostname, if a hostname is specified
|
||||
## which may be an IP address or a hostname, if a hostname is specified
|
||||
## this function will try each IP of that hostname.
|
||||
##
|
||||
## **Note:** This proc is not available for SSL sockets.
|
||||
@@ -1654,7 +1654,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||
hints.ai_socktype = toInt(SOCK_STREAM)
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
gaiNim(address, port, hints, aiList)
|
||||
|
||||
|
||||
# try all possibilities:
|
||||
var success = false
|
||||
var it = aiList
|
||||
@@ -1668,7 +1668,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
|
||||
proc sendTo*(socket: Socket, address: string, port: Port,
|
||||
proc sendTo*(socket: Socket, address: string, port: Port,
|
||||
data: string): int {.tags: [WriteIOEffect].} =
|
||||
## Friendlier version of the low-level ``sendTo``.
|
||||
result = socket.sendTo(address, port, cstring(data), data.len)
|
||||
@@ -1677,10 +1677,10 @@ when defined(Windows):
|
||||
const
|
||||
IOCPARM_MASK = 127
|
||||
IOC_IN = int(-2147483648)
|
||||
FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
||||
FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
||||
(102 shl 8) or 126
|
||||
|
||||
proc ioctlsocket(s: SocketHandle, cmd: clong,
|
||||
proc ioctlsocket(s: SocketHandle, cmd: clong,
|
||||
argptr: ptr clong): cint {.
|
||||
stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
|
||||
|
||||
@@ -1713,7 +1713,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
|
||||
## the connection to the server to be made.
|
||||
let originalStatus = not socket.nonblocking
|
||||
socket.setBlocking(false)
|
||||
|
||||
|
||||
socket.connectAsync(address, port, af)
|
||||
var s: seq[Socket] = @[socket]
|
||||
if selectWrite(s, timeout) != 1:
|
||||
|
||||
@@ -77,7 +77,7 @@ proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
|
||||
result = if not negative: j-1 else: a.len-j
|
||||
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
|
||||
var name = ""
|
||||
while f[i] in PatternChars:
|
||||
while f[i] in PatternChars:
|
||||
name.add(f[i])
|
||||
inc(i)
|
||||
result = findNormalized(name, a)+1
|
||||
@@ -131,7 +131,7 @@ proc scanBranch(p: var FormatParser, a: openArray[string],
|
||||
while true:
|
||||
case f[i]
|
||||
of ']': break
|
||||
of '|':
|
||||
of '|':
|
||||
inc i
|
||||
elsePart = i
|
||||
inc c
|
||||
@@ -172,7 +172,7 @@ proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
|
||||
var slice = false
|
||||
var i = p.i
|
||||
var f = p.f
|
||||
|
||||
|
||||
if f[i] == '{': inc i
|
||||
else: raiseInvalidFormat("'{' expected")
|
||||
if f[i] == '.' and f[i+1] == '.':
|
||||
@@ -193,12 +193,12 @@ proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
|
||||
if f[i] != '}': raiseInvalidFormat("'}' expected")
|
||||
inc i
|
||||
p.i = i
|
||||
|
||||
|
||||
proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
var i = p.i
|
||||
var f = p.f
|
||||
case f[i]
|
||||
of '$':
|
||||
of '$':
|
||||
emitChar p, s, '$'
|
||||
inc i
|
||||
of '*':
|
||||
@@ -232,7 +232,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
# $' '~{1..3}
|
||||
# insert space followed by 1..3 if not empty
|
||||
inc i
|
||||
call:
|
||||
call:
|
||||
let (x, y) = scanSlice(p, a)
|
||||
var L = 0
|
||||
for j in x..y: inc L, a[j].len
|
||||
@@ -258,7 +258,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
of 'i':
|
||||
inc i
|
||||
callNoLineLenTracking: scanQuote(p, indent, true)
|
||||
|
||||
|
||||
call:
|
||||
let (x, y) = scanSlice(p, a)
|
||||
if maxLen < 1: emitStrLinear(p, s, indent)
|
||||
@@ -266,7 +266,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
emitStr p, s, a[x]
|
||||
for j in x+1..y:
|
||||
emitStr p, s, sep
|
||||
if items >= maxLen:
|
||||
if items >= maxLen:
|
||||
emitStrLinear p, s, indent
|
||||
items = 0
|
||||
emitStr p, s, a[j]
|
||||
@@ -274,7 +274,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
of 'c':
|
||||
inc i
|
||||
callNoLineLenTracking: scanQuote(p, indent, true)
|
||||
|
||||
|
||||
call:
|
||||
let (x, y) = scanSlice(p, a)
|
||||
if p.lineLen + a[x].len > maxLen: emitStrLinear(p, s, indent)
|
||||
@@ -283,7 +283,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
emitStr p, s, sep
|
||||
if p.lineLen + a[j].len > maxLen: emitStrLinear(p, s, indent)
|
||||
emitStr p, s, a[j]
|
||||
|
||||
|
||||
else: raiseInvalidFormat("unit 'c' (chars) or 'i' (items) expected")
|
||||
break StringJoin
|
||||
|
||||
@@ -294,7 +294,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
emitStr p, s, sep
|
||||
emitStr p, s, a[j]
|
||||
else:
|
||||
call:
|
||||
call:
|
||||
var x = getFormatArg(p, a)
|
||||
emitStr p, s, a[x]
|
||||
p.i = i
|
||||
@@ -375,38 +375,38 @@ when isMainModule:
|
||||
doAssert "$1($', '{2..})" % ["f", "a", "b"] == "f(a, b)"
|
||||
|
||||
doAssert "$[$1($', '{2..})|''''|fg'$3']1" % ["7", "a", "b"] == "fg$3"
|
||||
|
||||
|
||||
doAssert "$[$#($', '{#..})|''''|$3]1" % ["0", "a", "b"] == "0(a, b)"
|
||||
doAssert "$' '~{..}" % "" == ""
|
||||
doAssert "$' '~{..}" % "P0" == " P0"
|
||||
doAssert "${$1}" % "1" == "1"
|
||||
doAssert "${$$-1} $$1" % "1" == "1 $1"
|
||||
|
||||
|
||||
doAssert "$#($', '10c'\n '{#..})" % ["doAssert", "longishA", "longish"] ==
|
||||
"""doAssert(
|
||||
longishA,
|
||||
longishA,
|
||||
longish)"""
|
||||
|
||||
|
||||
assert "type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
|
||||
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] ==
|
||||
strutils.unindent """
|
||||
type MyEnum* = enum
|
||||
fieldA, fieldB,
|
||||
FiledClkad, fieldD,
|
||||
fieldA, fieldB,
|
||||
FiledClkad, fieldD,
|
||||
fieldE, longishFieldName"""
|
||||
|
||||
|
||||
doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
|
||||
|
||||
|
||||
doAssert subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
|
||||
|
||||
|
||||
doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
|
||||
|
||||
|
||||
assert subex("type\n Enum = enum\n $', '40c'\n '{..}") % [
|
||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] ==
|
||||
strutils.unindent """
|
||||
type
|
||||
Enum = enum
|
||||
fieldNameA, fieldNameB, fieldNameC,
|
||||
fieldNameA, fieldNameB, fieldNameC,
|
||||
fieldNameD"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1285,7 +1285,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
|
||||
result = a.len - b.len
|
||||
|
||||
proc reversed*(s: string): string =
|
||||
## Returns the reverse of ``s``, interpreting it as Unicode characters.
|
||||
## Returns the reverse of ``s``, interpreting it as Unicode characters.
|
||||
## Unicode combining characters are correctly interpreted as well:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module is based on Python's Unidecode module by Tomaz Solc,
|
||||
## which in turn is based on the ``Text::Unidecode`` Perl module by
|
||||
## Sean M. Burke
|
||||
## This module is based on Python's Unidecode module by Tomaz Solc,
|
||||
## which in turn is based on the ``Text::Unidecode`` Perl module by
|
||||
## Sean M. Burke
|
||||
## (http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm ).
|
||||
##
|
||||
## It provides a single proc that does Unicode to ASCII transliterations:
|
||||
## It finds the sequence of ASCII characters that is the closest approximation
|
||||
## to the Unicode string.
|
||||
##
|
||||
## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some
|
||||
## information is lost in this transformation, of course, since several Unicode
|
||||
## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some
|
||||
## information is lost in this transformation, of course, since several Unicode
|
||||
## strings can be transformed in the same ASCII representation. So this is a
|
||||
## strictly one-way transformation. However a human reader will probably
|
||||
## strictly one-way transformation. However a human reader will probably
|
||||
## still be able to guess what original string was meant from the context.
|
||||
##
|
||||
## This module needs the data file "unidecode.dat" to work: You can either
|
||||
@@ -31,7 +31,7 @@ import unicode
|
||||
|
||||
when defined(embedUnidecodeTable):
|
||||
import strutils
|
||||
|
||||
|
||||
const translationTable = splitLines(slurp"unidecode/unidecode.dat")
|
||||
else:
|
||||
# shared is fine for threading:
|
||||
@@ -49,12 +49,12 @@ proc loadUnidecodeTable*(datafile = "unidecode.dat") =
|
||||
translationTable[i] = line.string
|
||||
inc(i)
|
||||
|
||||
proc unidecode*(s: string): string =
|
||||
proc unidecode*(s: string): string =
|
||||
## Finds the sequence of ASCII characters that is the closest approximation
|
||||
## to the UTF-8 string `s`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## ..code-block:: nim
|
||||
##
|
||||
## unidecode("\x53\x17\x4E\xB0")
|
||||
@@ -63,7 +63,7 @@ proc unidecode*(s: string): string =
|
||||
##
|
||||
assert(not isNil(translationTable))
|
||||
result = ""
|
||||
for r in runes(s):
|
||||
for r in runes(s):
|
||||
var c = int(r)
|
||||
if c <=% 127: add(result, chr(c))
|
||||
elif c <% translationTable.len: add(result, translationTable[c-128])
|
||||
|
||||
@@ -30,19 +30,19 @@ proc getNS(prefix: string): string =
|
||||
if ":" in key:
|
||||
if key.split(':')[1] == prefix:
|
||||
return value
|
||||
|
||||
|
||||
if key == "xmlns":
|
||||
defaultNS.add(value)
|
||||
|
||||
|
||||
# Don't return the default namespaces
|
||||
# in the loop, because then they would have a precedence
|
||||
# over normal namespaces
|
||||
if defaultNS.len() > 0:
|
||||
return defaultNS[0] # Return the first found default namespace
|
||||
# if none are specified for this prefix
|
||||
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
proc parseText(x: var XmlParser, doc: var PDocument): PText =
|
||||
result = doc.createTextNode(x.charData())
|
||||
|
||||
@@ -58,19 +58,19 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
|
||||
n.appendChild(parseElement(x, doc))
|
||||
else:
|
||||
n = doc.createElementNS("", x.elementName)
|
||||
|
||||
|
||||
of xmlElementEnd:
|
||||
if x.elementName == n.nodeName:
|
||||
# n.normalize() # Remove any whitespace etc.
|
||||
|
||||
|
||||
var ns: string
|
||||
if x.elementName.contains(':'):
|
||||
ns = getNS(x.elementName.split(':')[0])
|
||||
else:
|
||||
ns = getNS("")
|
||||
|
||||
|
||||
n.namespaceURI = ns
|
||||
|
||||
|
||||
# Remove any namespaces this element declared
|
||||
var count = 0 # Variable which keeps the index
|
||||
# We need to edit it..
|
||||
@@ -82,15 +82,15 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
|
||||
|
||||
return n
|
||||
else: #The wrong element is ended
|
||||
raise newException(EMismatchedTag, "Mismatched tag at line " &
|
||||
raise newException(EMismatchedTag, "Mismatched tag at line " &
|
||||
$x.getLine() & " column " & $x.getColumn)
|
||||
|
||||
|
||||
of xmlCharData:
|
||||
n.appendChild(parseText(x, doc))
|
||||
of xmlAttribute:
|
||||
if x.attrKey == "xmlns" or x.attrKey.startsWith("xmlns:"):
|
||||
nsList.add((x.attrKey, x.attrValue, n))
|
||||
|
||||
|
||||
if x.attrKey.contains(':'):
|
||||
var ns = getNS(x.attrKey)
|
||||
n.setAttributeNS(ns, x.attrKey, x.attrValue)
|
||||
@@ -103,7 +103,7 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
|
||||
n.appendChild(doc.createComment(x.charData()))
|
||||
of xmlPI:
|
||||
n.appendChild(doc.createProcessingInstruction(x.piName(), x.piRest()))
|
||||
|
||||
|
||||
of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial:
|
||||
discard " Unused \'events\'"
|
||||
|
||||
@@ -111,19 +111,19 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
|
||||
raise newException(EParserError, "Unexpected XML Parser event")
|
||||
x.next()
|
||||
|
||||
raise newException(EMismatchedTag,
|
||||
raise newException(EMismatchedTag,
|
||||
"Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn)
|
||||
|
||||
proc loadXMLStream*(stream: Stream): PDocument =
|
||||
## Loads and parses XML from a stream specified by ``stream``, and returns
|
||||
## Loads and parses XML from a stream specified by ``stream``, and returns
|
||||
## a ``PDocument``
|
||||
|
||||
var x: XmlParser
|
||||
open(x, stream, nil, {reportComments})
|
||||
|
||||
|
||||
var xmlDoc: PDocument
|
||||
var dom: PDOMImplementation = getDOM()
|
||||
|
||||
|
||||
while true:
|
||||
x.next()
|
||||
case x.kind()
|
||||
@@ -140,16 +140,16 @@ proc loadXMLStream*(stream: Stream): PDocument =
|
||||
return xmlDoc
|
||||
|
||||
proc loadXML*(xml: string): PDocument =
|
||||
## Loads and parses XML from a string specified by ``xml``, and returns
|
||||
## Loads and parses XML from a string specified by ``xml``, and returns
|
||||
## a ``PDocument``
|
||||
var s = newStringStream(xml)
|
||||
return loadXMLStream(s)
|
||||
|
||||
|
||||
|
||||
|
||||
proc loadXMLFile*(path: string): PDocument =
|
||||
## Loads and parses XML from a file specified by ``path``, and returns
|
||||
## Loads and parses XML from a file specified by ``path``, and returns
|
||||
## a ``PDocument``
|
||||
|
||||
|
||||
var s = newFileStream(path, fmRead)
|
||||
if s == nil: raise newException(IOError, "Unable to read file " & path)
|
||||
return loadXMLStream(s)
|
||||
@@ -164,5 +164,5 @@ when not defined(testing) and isMainModule:
|
||||
if i.namespaceURI != nil:
|
||||
echo(i.nodeName, "=", i.namespaceURI)
|
||||
|
||||
|
||||
|
||||
echo($xml)
|
||||
|
||||
@@ -18,24 +18,24 @@ type
|
||||
|
||||
{.deprecated: [EInvalidXml: XmlError].}
|
||||
|
||||
proc raiseInvalidXml(errors: seq[string]) =
|
||||
proc raiseInvalidXml(errors: seq[string]) =
|
||||
var e: ref XmlError
|
||||
new(e)
|
||||
e.msg = errors[0]
|
||||
e.errors = errors
|
||||
raise e
|
||||
|
||||
proc addNode(father, son: XmlNode) =
|
||||
proc addNode(father, son: XmlNode) =
|
||||
if son != nil: add(father, son)
|
||||
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
|
||||
|
||||
proc untilElementEnd(x: var XmlParser, result: XmlNode,
|
||||
proc untilElementEnd(x: var XmlParser, result: XmlNode,
|
||||
errors: var seq[string]) =
|
||||
while true:
|
||||
case x.kind
|
||||
of xmlElementEnd:
|
||||
if x.elementName == result.tag:
|
||||
of xmlElementEnd:
|
||||
if x.elementName == result.tag:
|
||||
next(x)
|
||||
else:
|
||||
errors.add(errorMsg(x, "</" & result.tag & "> expected"))
|
||||
@@ -49,7 +49,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
|
||||
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
case x.kind
|
||||
of xmlComment:
|
||||
of xmlComment:
|
||||
result = newComment(x.charData)
|
||||
next(x)
|
||||
of xmlCharData, xmlWhitespace:
|
||||
@@ -67,11 +67,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
untilElementEnd(x, result, errors)
|
||||
of xmlElementEnd:
|
||||
errors.add(errorMsg(x, "unexpected ending tag: " & x.elementName))
|
||||
of xmlElementOpen:
|
||||
of xmlElementOpen:
|
||||
result = newElement(x.elementName)
|
||||
next(x)
|
||||
result.attrs = newStringTable()
|
||||
while true:
|
||||
while true:
|
||||
case x.kind
|
||||
of xmlAttribute:
|
||||
result.attrs[x.attrKey] = x.attrValue
|
||||
@@ -91,7 +91,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
of xmlAttribute, xmlElementClose:
|
||||
errors.add(errorMsg(x, "<some_tag> expected"))
|
||||
next(x)
|
||||
of xmlCData:
|
||||
of xmlCData:
|
||||
result = newCData(x.charData)
|
||||
next(x)
|
||||
of xmlEntity:
|
||||
@@ -100,8 +100,8 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
next(x)
|
||||
of xmlEof: discard
|
||||
|
||||
proc parseXml*(s: Stream, filename: string,
|
||||
errors: var seq[string]): XmlNode =
|
||||
proc parseXml*(s: Stream, filename: string,
|
||||
errors: var seq[string]): XmlNode =
|
||||
## parses the XML from stream `s` and returns a ``PXmlNode``. Every
|
||||
## occurred parsing error is added to the `errors` sequence.
|
||||
var x: XmlParser
|
||||
@@ -109,7 +109,7 @@ proc parseXml*(s: Stream, filename: string,
|
||||
while true:
|
||||
x.next()
|
||||
case x.kind
|
||||
of xmlElementOpen, xmlElementStart:
|
||||
of xmlElementOpen, xmlElementStart:
|
||||
result = parse(x, errors)
|
||||
break
|
||||
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
|
||||
@@ -120,7 +120,7 @@ proc parseXml*(s: Stream, filename: string,
|
||||
break
|
||||
close(x)
|
||||
|
||||
proc parseXml*(s: Stream): XmlNode =
|
||||
proc parseXml*(s: Stream): XmlNode =
|
||||
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
|
||||
## errors are turned into an ``EInvalidXML`` exception.
|
||||
var errors: seq[string] = @[]
|
||||
@@ -128,7 +128,7 @@ proc parseXml*(s: Stream): XmlNode =
|
||||
if errors.len > 0: raiseInvalidXml(errors)
|
||||
|
||||
proc loadXml*(path: string, errors: var seq[string]): XmlNode =
|
||||
## Loads and parses XML from file specified by ``path``, and returns
|
||||
## Loads and parses XML from file specified by ``path``, and returns
|
||||
## a ``PXmlNode``. Every occurred parsing error is added to the `errors`
|
||||
## sequence.
|
||||
var s = newFileStream(path, fmRead)
|
||||
@@ -136,9 +136,9 @@ proc loadXml*(path: string, errors: var seq[string]): XmlNode =
|
||||
result = parseXml(s, path, errors)
|
||||
|
||||
proc loadXml*(path: string): XmlNode =
|
||||
## Loads and parses XML from file specified by ``path``, and returns
|
||||
## Loads and parses XML from file specified by ``path``, and returns
|
||||
## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML``
|
||||
## exception.
|
||||
## exception.
|
||||
var errors: seq[string] = @[]
|
||||
result = loadXml(path, errors)
|
||||
if errors.len > 0: raiseInvalidXml(errors)
|
||||
@@ -146,14 +146,14 @@ proc loadXml*(path: string): XmlNode =
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
|
||||
var errors: seq[string] = @[]
|
||||
var errors: seq[string] = @[]
|
||||
var x = loadXml(paramStr(1), errors)
|
||||
for e in items(errors): echo e
|
||||
|
||||
|
||||
var f: File
|
||||
if open(f, "xmltest.txt", fmWrite):
|
||||
f.write($x)
|
||||
f.close()
|
||||
else:
|
||||
quit("cannot write test.txt")
|
||||
|
||||
|
||||
|
||||
@@ -13,20 +13,20 @@
|
||||
|
||||
{.push hints:off}
|
||||
|
||||
proc c_strcmp(a, b: cstring): cint {.header: "<string.h>",
|
||||
proc c_strcmp(a, b: cstring): cint {.header: "<string.h>",
|
||||
noSideEffect, importc: "strcmp".}
|
||||
proc c_memcmp(a, b: cstring, size: int): cint {.header: "<string.h>",
|
||||
proc c_memcmp(a, b: cstring, size: int): cint {.header: "<string.h>",
|
||||
noSideEffect, importc: "memcmp".}
|
||||
proc c_memcpy(a, b: cstring, size: int) {.header: "<string.h>", importc: "memcpy".}
|
||||
proc c_strlen(a: cstring): int {.header: "<string.h>",
|
||||
proc c_strlen(a: cstring): int {.header: "<string.h>",
|
||||
noSideEffect, importc: "strlen".}
|
||||
proc c_memset(p: pointer, value: cint, size: int) {.
|
||||
header: "<string.h>", importc: "memset".}
|
||||
|
||||
type
|
||||
C_TextFile {.importc: "FILE", header: "<stdio.h>",
|
||||
C_TextFile {.importc: "FILE", header: "<stdio.h>",
|
||||
final, incompleteStruct.} = object
|
||||
C_BinaryFile {.importc: "FILE", header: "<stdio.h>",
|
||||
C_BinaryFile {.importc: "FILE", header: "<stdio.h>",
|
||||
final, incompleteStruct.} = object
|
||||
C_TextFileStar = ptr C_TextFile
|
||||
C_BinaryFileStar = ptr C_BinaryFile
|
||||
@@ -101,15 +101,15 @@ proc c_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.
|
||||
importc: "signal", header: "<signal.h>".}
|
||||
proc c_raise(sig: cint) {.importc: "raise", header: "<signal.h>".}
|
||||
|
||||
proc c_fputs(c: cstring, f: C_TextFileStar) {.importc: "fputs",
|
||||
proc c_fputs(c: cstring, f: C_TextFileStar) {.importc: "fputs",
|
||||
header: "<stdio.h>".}
|
||||
proc c_fgets(c: cstring, n: int, f: C_TextFileStar): cstring {.
|
||||
importc: "fgets", header: "<stdio.h>".}
|
||||
proc c_fgetc(stream: C_TextFileStar): int {.importc: "fgetc",
|
||||
proc c_fgetc(stream: C_TextFileStar): int {.importc: "fgetc",
|
||||
header: "<stdio.h>".}
|
||||
proc c_ungetc(c: int, f: C_TextFileStar) {.importc: "ungetc",
|
||||
proc c_ungetc(c: int, f: C_TextFileStar) {.importc: "ungetc",
|
||||
header: "<stdio.h>".}
|
||||
proc c_putc(c: char, stream: C_TextFileStar) {.importc: "putc",
|
||||
proc c_putc(c: char, stream: C_TextFileStar) {.importc: "putc",
|
||||
header: "<stdio.h>".}
|
||||
proc c_fprintf(f: C_TextFileStar, frmt: cstring) {.
|
||||
importc: "fprintf", header: "<stdio.h>", varargs.}
|
||||
@@ -120,7 +120,7 @@ proc c_fopen(filename, mode: cstring): C_TextFileStar {.
|
||||
importc: "fopen", header: "<stdio.h>".}
|
||||
proc c_fclose(f: C_TextFileStar) {.importc: "fclose", header: "<stdio.h>".}
|
||||
|
||||
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
||||
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
||||
importc: "sprintf", varargs, noSideEffect.}
|
||||
# we use it only in a way that cannot lead to security issues
|
||||
|
||||
@@ -149,7 +149,7 @@ when hostOS != "standalone":
|
||||
when not declared(errno):
|
||||
when defined(NimrodVM):
|
||||
var vmErrnoWrapper {.importc.}: ptr cint
|
||||
template errno: expr =
|
||||
template errno: expr =
|
||||
bind vmErrnoWrapper
|
||||
vmErrnoWrapper[]
|
||||
else:
|
||||
|
||||
@@ -16,7 +16,7 @@ proc lowGauge(n: PAvlNode): int =
|
||||
while not isBottom(it):
|
||||
result = it.key
|
||||
it = it.link[0]
|
||||
|
||||
|
||||
proc highGauge(n: PAvlNode): int =
|
||||
result = -1
|
||||
var it = n
|
||||
@@ -24,7 +24,7 @@ proc highGauge(n: PAvlNode): int =
|
||||
result = it.upperBound
|
||||
it = it.link[1]
|
||||
|
||||
proc find(root: PAvlNode, key: int): PAvlNode =
|
||||
proc find(root: PAvlNode, key: int): PAvlNode =
|
||||
var it = root
|
||||
while not isBottom(it):
|
||||
if it.key == key: return it
|
||||
|
||||
@@ -65,7 +65,7 @@ proc init(s: var CellSeq, cap: int = 1024) =
|
||||
s.cap = cap
|
||||
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
|
||||
|
||||
proc deinit(s: var CellSeq) =
|
||||
proc deinit(s: var CellSeq) =
|
||||
dealloc(s.d)
|
||||
s.d = nil
|
||||
s.len = 0
|
||||
@@ -98,7 +98,7 @@ proc nextTry(h, maxHash: int): int {.inline.} =
|
||||
# For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
# random-number generation for proof).
|
||||
|
||||
|
||||
proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while t.data[h] != nil:
|
||||
@@ -170,16 +170,16 @@ proc excl(s: var CellSet, cell: PCell) =
|
||||
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
|
||||
not (1 shl (u and IntMask)))
|
||||
|
||||
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
|
||||
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
|
||||
var u = cast[ByteAddress](cell)
|
||||
var t = cellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
u = (u %% PageSize) /% MemAlign
|
||||
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||
if not result:
|
||||
if not result:
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
|
||||
(1 shl (u and IntMask))
|
||||
else:
|
||||
else:
|
||||
incl(s, cell)
|
||||
result = false
|
||||
|
||||
|
||||
@@ -1,267 +1,267 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Channel support for threads. **Note**: This is part of the system module.
|
||||
## Do not import it directly. To activate thread support you need to compile
|
||||
## with the ``--threads:on`` command line switch.
|
||||
##
|
||||
## **Note:** The current implementation of message passing is slow and does
|
||||
## not work with cyclic data structures.
|
||||
|
||||
when not declared(NimString):
|
||||
{.error: "You must not import this module explicitly".}
|
||||
|
||||
type
|
||||
pbytes = ptr array[0.. 0xffff, byte]
|
||||
RawChannel {.pure, final.} = object ## msg queue for a thread
|
||||
rd, wr, count, mask: int
|
||||
data: pbytes
|
||||
lock: SysLock
|
||||
cond: SysCond
|
||||
elemType: PNimType
|
||||
ready: bool
|
||||
region: MemRegion
|
||||
PRawChannel = ptr RawChannel
|
||||
LoadStoreMode = enum mStore, mLoad
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Channel support for threads. **Note**: This is part of the system module.
|
||||
## Do not import it directly. To activate thread support you need to compile
|
||||
## with the ``--threads:on`` command line switch.
|
||||
##
|
||||
## **Note:** The current implementation of message passing is slow and does
|
||||
## not work with cyclic data structures.
|
||||
|
||||
when not declared(NimString):
|
||||
{.error: "You must not import this module explicitly".}
|
||||
|
||||
type
|
||||
pbytes = ptr array[0.. 0xffff, byte]
|
||||
RawChannel {.pure, final.} = object ## msg queue for a thread
|
||||
rd, wr, count, mask: int
|
||||
data: pbytes
|
||||
lock: SysLock
|
||||
cond: SysCond
|
||||
elemType: PNimType
|
||||
ready: bool
|
||||
region: MemRegion
|
||||
PRawChannel = ptr RawChannel
|
||||
LoadStoreMode = enum mStore, mLoad
|
||||
Channel* {.gcsafe.}[TMsg] = RawChannel ## a channel for thread communication
|
||||
{.deprecated: [TRawChannel: RawChannel, TLoadStoreMode: LoadStoreMode,
|
||||
TChannel: Channel].}
|
||||
|
||||
const ChannelDeadMask = -2
|
||||
|
||||
proc initRawChannel(p: pointer) =
|
||||
var c = cast[PRawChannel](p)
|
||||
initSysLock(c.lock)
|
||||
initSysCond(c.cond)
|
||||
c.mask = -1
|
||||
|
||||
proc deinitRawChannel(p: pointer) =
|
||||
var c = cast[PRawChannel](p)
|
||||
# we need to grab the lock to be safe against sending threads!
|
||||
acquireSys(c.lock)
|
||||
c.mask = ChannelDeadMask
|
||||
deallocOsPages(c.region)
|
||||
deinitSys(c.lock)
|
||||
deinitSysCond(c.cond)
|
||||
|
||||
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
||||
mode: LoadStoreMode) {.benign.}
|
||||
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
|
||||
mode: LoadStoreMode) {.benign.} =
|
||||
var
|
||||
d = cast[ByteAddress](dest)
|
||||
s = cast[ByteAddress](src)
|
||||
case n.kind
|
||||
of nkSlot: storeAux(cast[pointer](d +% n.offset),
|
||||
cast[pointer](s +% n.offset), n.typ, t, mode)
|
||||
of nkList:
|
||||
for i in 0..n.len-1: storeAux(dest, src, n.sons[i], t, mode)
|
||||
of nkCase:
|
||||
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
|
||||
n.typ.size)
|
||||
var m = selectBranch(src, n)
|
||||
if m != nil: storeAux(dest, src, m, t, mode)
|
||||
of nkNone: sysAssert(false, "storeAux")
|
||||
|
||||
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
||||
mode: LoadStoreMode) =
|
||||
var
|
||||
d = cast[ByteAddress](dest)
|
||||
s = cast[ByteAddress](src)
|
||||
sysAssert(mt != nil, "mt == nil")
|
||||
case mt.kind
|
||||
of tyString:
|
||||
if mode == mStore:
|
||||
var x = cast[PPointer](dest)
|
||||
var s2 = cast[PPointer](s)[]
|
||||
if s2 == nil:
|
||||
x[] = nil
|
||||
else:
|
||||
var ss = cast[NimString](s2)
|
||||
var ns = cast[NimString](alloc(t.region, ss.len+1 + GenericSeqSize))
|
||||
copyMem(ns, ss, ss.len+1 + GenericSeqSize)
|
||||
x[] = ns
|
||||
else:
|
||||
var x = cast[PPointer](dest)
|
||||
var s2 = cast[PPointer](s)[]
|
||||
if s2 == nil:
|
||||
unsureAsgnRef(x, s2)
|
||||
else:
|
||||
unsureAsgnRef(x, copyString(cast[NimString](s2)))
|
||||
dealloc(t.region, s2)
|
||||
of tySequence:
|
||||
var s2 = cast[PPointer](src)[]
|
||||
var seq = cast[PGenericSeq](s2)
|
||||
var x = cast[PPointer](dest)
|
||||
if s2 == nil:
|
||||
if mode == mStore:
|
||||
x[] = nil
|
||||
else:
|
||||
unsureAsgnRef(x, nil)
|
||||
else:
|
||||
sysAssert(dest != nil, "dest == nil")
|
||||
if mode == mStore:
|
||||
x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize)
|
||||
else:
|
||||
unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize))
|
||||
var dst = cast[ByteAddress](cast[PPointer](dest)[])
|
||||
for i in 0..seq.len-1:
|
||||
storeAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base, t, mode)
|
||||
var dstseq = cast[PGenericSeq](dst)
|
||||
dstseq.len = seq.len
|
||||
dstseq.reserved = seq.len
|
||||
if mode != mStore: dealloc(t.region, s2)
|
||||
of tyObject:
|
||||
# copy type field:
|
||||
var pint = cast[ptr PNimType](dest)
|
||||
# XXX use dynamic type here!
|
||||
pint[] = mt
|
||||
if mt.base != nil:
|
||||
storeAux(dest, src, mt.base, t, mode)
|
||||
storeAux(dest, src, mt.node, t, mode)
|
||||
of tyTuple:
|
||||
storeAux(dest, src, mt.node, t, mode)
|
||||
of tyArray, tyArrayConstr:
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
storeAux(cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
|
||||
of tyRef:
|
||||
var s = cast[PPointer](src)[]
|
||||
var x = cast[PPointer](dest)
|
||||
if s == nil:
|
||||
if mode == mStore:
|
||||
x[] = nil
|
||||
else:
|
||||
unsureAsgnRef(x, nil)
|
||||
else:
|
||||
if mode == mStore:
|
||||
x[] = alloc(t.region, mt.base.size)
|
||||
else:
|
||||
# XXX we should use the dynamic type here too, but that is not stored
|
||||
# in the inbox at all --> use source[]'s object type? but how? we need
|
||||
# a tyRef to the object!
|
||||
var obj = newObj(mt, mt.base.size)
|
||||
unsureAsgnRef(x, obj)
|
||||
storeAux(x[], s, mt.base, t, mode)
|
||||
if mode != mStore: dealloc(t.region, s)
|
||||
else:
|
||||
copyMem(dest, src, mt.size) # copy raw bits
|
||||
|
||||
proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
|
||||
## adds an `item` to the end of the queue `q`.
|
||||
var cap = q.mask+1
|
||||
if q.count >= cap:
|
||||
# start with capacity for 2 entries in the queue:
|
||||
if cap == 0: cap = 1
|
||||
var n = cast[pbytes](alloc0(q.region, cap*2*typ.size))
|
||||
var z = 0
|
||||
var i = q.rd
|
||||
var c = q.count
|
||||
while c > 0:
|
||||
dec c
|
||||
copyMem(addr(n[z*typ.size]), addr(q.data[i*typ.size]), typ.size)
|
||||
i = (i + 1) and q.mask
|
||||
inc z
|
||||
if q.data != nil: dealloc(q.region, q.data)
|
||||
q.data = n
|
||||
q.mask = cap*2 - 1
|
||||
q.wr = q.count
|
||||
q.rd = 0
|
||||
storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
|
||||
inc q.count
|
||||
q.wr = (q.wr + 1) and q.mask
|
||||
|
||||
proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
|
||||
sysAssert q.count > 0, "rawRecv"
|
||||
dec q.count
|
||||
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
|
||||
q.rd = (q.rd + 1) and q.mask
|
||||
|
||||
template lockChannel(q: expr, action: stmt) {.immediate.} =
|
||||
acquireSys(q.lock)
|
||||
action
|
||||
releaseSys(q.lock)
|
||||
|
||||
template sendImpl(q: expr) {.immediate.} =
|
||||
if q.mask == ChannelDeadMask:
|
||||
sysFatal(DeadThreadError, "cannot send message; thread died")
|
||||
acquireSys(q.lock)
|
||||
var m: TMsg
|
||||
shallowCopy(m, msg)
|
||||
var typ = cast[PNimType](getTypeInfo(msg))
|
||||
rawSend(q, addr(m), typ)
|
||||
q.elemType = typ
|
||||
releaseSys(q.lock)
|
||||
signalSysCond(q.cond)
|
||||
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
|
||||
## sends a message to a thread. `msg` is deeply copied.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
sendImpl(q)
|
||||
|
||||
proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
|
||||
# to save space, the generic is as small as possible
|
||||
q.ready = true
|
||||
while q.count <= 0:
|
||||
waitSysCond(q.cond, q.lock)
|
||||
q.ready = false
|
||||
if typ != q.elemType:
|
||||
releaseSys(q.lock)
|
||||
sysFatal(ValueError, "cannot receive message of wrong type")
|
||||
rawRecv(q, res, typ)
|
||||
|
||||
proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
|
||||
## receives a message from the channel `c`. This blocks until
|
||||
## a message has arrived! You may use ``peek`` to avoid the blocking.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
acquireSys(q.lock)
|
||||
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
|
||||
releaseSys(q.lock)
|
||||
|
||||
proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
|
||||
msg: TMsg] =
|
||||
## try to receives a message from the channel `c` if available. Otherwise
|
||||
## it returns ``(false, default(msg))``.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
if q.mask != ChannelDeadMask:
|
||||
const ChannelDeadMask = -2
|
||||
|
||||
proc initRawChannel(p: pointer) =
|
||||
var c = cast[PRawChannel](p)
|
||||
initSysLock(c.lock)
|
||||
initSysCond(c.cond)
|
||||
c.mask = -1
|
||||
|
||||
proc deinitRawChannel(p: pointer) =
|
||||
var c = cast[PRawChannel](p)
|
||||
# we need to grab the lock to be safe against sending threads!
|
||||
acquireSys(c.lock)
|
||||
c.mask = ChannelDeadMask
|
||||
deallocOsPages(c.region)
|
||||
deinitSys(c.lock)
|
||||
deinitSysCond(c.cond)
|
||||
|
||||
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
||||
mode: LoadStoreMode) {.benign.}
|
||||
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
|
||||
mode: LoadStoreMode) {.benign.} =
|
||||
var
|
||||
d = cast[ByteAddress](dest)
|
||||
s = cast[ByteAddress](src)
|
||||
case n.kind
|
||||
of nkSlot: storeAux(cast[pointer](d +% n.offset),
|
||||
cast[pointer](s +% n.offset), n.typ, t, mode)
|
||||
of nkList:
|
||||
for i in 0..n.len-1: storeAux(dest, src, n.sons[i], t, mode)
|
||||
of nkCase:
|
||||
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
|
||||
n.typ.size)
|
||||
var m = selectBranch(src, n)
|
||||
if m != nil: storeAux(dest, src, m, t, mode)
|
||||
of nkNone: sysAssert(false, "storeAux")
|
||||
|
||||
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
||||
mode: LoadStoreMode) =
|
||||
var
|
||||
d = cast[ByteAddress](dest)
|
||||
s = cast[ByteAddress](src)
|
||||
sysAssert(mt != nil, "mt == nil")
|
||||
case mt.kind
|
||||
of tyString:
|
||||
if mode == mStore:
|
||||
var x = cast[PPointer](dest)
|
||||
var s2 = cast[PPointer](s)[]
|
||||
if s2 == nil:
|
||||
x[] = nil
|
||||
else:
|
||||
var ss = cast[NimString](s2)
|
||||
var ns = cast[NimString](alloc(t.region, ss.len+1 + GenericSeqSize))
|
||||
copyMem(ns, ss, ss.len+1 + GenericSeqSize)
|
||||
x[] = ns
|
||||
else:
|
||||
var x = cast[PPointer](dest)
|
||||
var s2 = cast[PPointer](s)[]
|
||||
if s2 == nil:
|
||||
unsureAsgnRef(x, s2)
|
||||
else:
|
||||
unsureAsgnRef(x, copyString(cast[NimString](s2)))
|
||||
dealloc(t.region, s2)
|
||||
of tySequence:
|
||||
var s2 = cast[PPointer](src)[]
|
||||
var seq = cast[PGenericSeq](s2)
|
||||
var x = cast[PPointer](dest)
|
||||
if s2 == nil:
|
||||
if mode == mStore:
|
||||
x[] = nil
|
||||
else:
|
||||
unsureAsgnRef(x, nil)
|
||||
else:
|
||||
sysAssert(dest != nil, "dest == nil")
|
||||
if mode == mStore:
|
||||
x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize)
|
||||
else:
|
||||
unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize))
|
||||
var dst = cast[ByteAddress](cast[PPointer](dest)[])
|
||||
for i in 0..seq.len-1:
|
||||
storeAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base, t, mode)
|
||||
var dstseq = cast[PGenericSeq](dst)
|
||||
dstseq.len = seq.len
|
||||
dstseq.reserved = seq.len
|
||||
if mode != mStore: dealloc(t.region, s2)
|
||||
of tyObject:
|
||||
# copy type field:
|
||||
var pint = cast[ptr PNimType](dest)
|
||||
# XXX use dynamic type here!
|
||||
pint[] = mt
|
||||
if mt.base != nil:
|
||||
storeAux(dest, src, mt.base, t, mode)
|
||||
storeAux(dest, src, mt.node, t, mode)
|
||||
of tyTuple:
|
||||
storeAux(dest, src, mt.node, t, mode)
|
||||
of tyArray, tyArrayConstr:
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
storeAux(cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
|
||||
of tyRef:
|
||||
var s = cast[PPointer](src)[]
|
||||
var x = cast[PPointer](dest)
|
||||
if s == nil:
|
||||
if mode == mStore:
|
||||
x[] = nil
|
||||
else:
|
||||
unsureAsgnRef(x, nil)
|
||||
else:
|
||||
if mode == mStore:
|
||||
x[] = alloc(t.region, mt.base.size)
|
||||
else:
|
||||
# XXX we should use the dynamic type here too, but that is not stored
|
||||
# in the inbox at all --> use source[]'s object type? but how? we need
|
||||
# a tyRef to the object!
|
||||
var obj = newObj(mt, mt.base.size)
|
||||
unsureAsgnRef(x, obj)
|
||||
storeAux(x[], s, mt.base, t, mode)
|
||||
if mode != mStore: dealloc(t.region, s)
|
||||
else:
|
||||
copyMem(dest, src, mt.size) # copy raw bits
|
||||
|
||||
proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
|
||||
## adds an `item` to the end of the queue `q`.
|
||||
var cap = q.mask+1
|
||||
if q.count >= cap:
|
||||
# start with capacity for 2 entries in the queue:
|
||||
if cap == 0: cap = 1
|
||||
var n = cast[pbytes](alloc0(q.region, cap*2*typ.size))
|
||||
var z = 0
|
||||
var i = q.rd
|
||||
var c = q.count
|
||||
while c > 0:
|
||||
dec c
|
||||
copyMem(addr(n[z*typ.size]), addr(q.data[i*typ.size]), typ.size)
|
||||
i = (i + 1) and q.mask
|
||||
inc z
|
||||
if q.data != nil: dealloc(q.region, q.data)
|
||||
q.data = n
|
||||
q.mask = cap*2 - 1
|
||||
q.wr = q.count
|
||||
q.rd = 0
|
||||
storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
|
||||
inc q.count
|
||||
q.wr = (q.wr + 1) and q.mask
|
||||
|
||||
proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
|
||||
sysAssert q.count > 0, "rawRecv"
|
||||
dec q.count
|
||||
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
|
||||
q.rd = (q.rd + 1) and q.mask
|
||||
|
||||
template lockChannel(q: expr, action: stmt) {.immediate.} =
|
||||
acquireSys(q.lock)
|
||||
action
|
||||
releaseSys(q.lock)
|
||||
|
||||
template sendImpl(q: expr) {.immediate.} =
|
||||
if q.mask == ChannelDeadMask:
|
||||
sysFatal(DeadThreadError, "cannot send message; thread died")
|
||||
acquireSys(q.lock)
|
||||
var m: TMsg
|
||||
shallowCopy(m, msg)
|
||||
var typ = cast[PNimType](getTypeInfo(msg))
|
||||
rawSend(q, addr(m), typ)
|
||||
q.elemType = typ
|
||||
releaseSys(q.lock)
|
||||
signalSysCond(q.cond)
|
||||
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
|
||||
## sends a message to a thread. `msg` is deeply copied.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
sendImpl(q)
|
||||
|
||||
proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
|
||||
# to save space, the generic is as small as possible
|
||||
q.ready = true
|
||||
while q.count <= 0:
|
||||
waitSysCond(q.cond, q.lock)
|
||||
q.ready = false
|
||||
if typ != q.elemType:
|
||||
releaseSys(q.lock)
|
||||
sysFatal(ValueError, "cannot receive message of wrong type")
|
||||
rawRecv(q, res, typ)
|
||||
|
||||
proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
|
||||
## receives a message from the channel `c`. This blocks until
|
||||
## a message has arrived! You may use ``peek`` to avoid the blocking.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
acquireSys(q.lock)
|
||||
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
|
||||
releaseSys(q.lock)
|
||||
|
||||
proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
|
||||
msg: TMsg] =
|
||||
## try to receives a message from the channel `c` if available. Otherwise
|
||||
## it returns ``(false, default(msg))``.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
if q.mask != ChannelDeadMask:
|
||||
if tryAcquireSys(q.lock):
|
||||
if q.count > 0:
|
||||
llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
|
||||
result.dataAvailable = true
|
||||
releaseSys(q.lock)
|
||||
|
||||
proc peek*[TMsg](c: var Channel[TMsg]): int =
|
||||
## returns the current number of messages in the channel `c`. Returns -1
|
||||
## if the channel has been closed. **Note**: This is dangerous to use
|
||||
## as it encourages races. It's much better to use ``tryRecv`` instead.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
if q.mask != ChannelDeadMask:
|
||||
lockChannel(q):
|
||||
result = q.count
|
||||
else:
|
||||
result = -1
|
||||
|
||||
proc open*[TMsg](c: var Channel[TMsg]) =
|
||||
## opens a channel `c` for inter thread communication.
|
||||
initRawChannel(addr(c))
|
||||
|
||||
proc close*[TMsg](c: var Channel[TMsg]) =
|
||||
## closes a channel `c` and frees its associated resources.
|
||||
deinitRawChannel(addr(c))
|
||||
|
||||
proc ready*[TMsg](c: var Channel[TMsg]): bool =
|
||||
## returns true iff some thread is waiting on the channel `c` for
|
||||
## new messages.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
result = q.ready
|
||||
|
||||
if q.count > 0:
|
||||
llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
|
||||
result.dataAvailable = true
|
||||
releaseSys(q.lock)
|
||||
|
||||
proc peek*[TMsg](c: var Channel[TMsg]): int =
|
||||
## returns the current number of messages in the channel `c`. Returns -1
|
||||
## if the channel has been closed. **Note**: This is dangerous to use
|
||||
## as it encourages races. It's much better to use ``tryRecv`` instead.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
if q.mask != ChannelDeadMask:
|
||||
lockChannel(q):
|
||||
result = q.count
|
||||
else:
|
||||
result = -1
|
||||
|
||||
proc open*[TMsg](c: var Channel[TMsg]) =
|
||||
## opens a channel `c` for inter thread communication.
|
||||
initRawChannel(addr(c))
|
||||
|
||||
proc close*[TMsg](c: var Channel[TMsg]) =
|
||||
## closes a channel `c` and frees its associated resources.
|
||||
deinitRawChannel(addr(c))
|
||||
|
||||
proc ready*[TMsg](c: var Channel[TMsg]): bool =
|
||||
## returns true iff some thread is waiting on the channel `c` for
|
||||
## new messages.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
result = q.ready
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
|
||||
s = cast[ByteAddress](src)
|
||||
case n.kind
|
||||
of nkSlot:
|
||||
genericDeepCopyAux(cast[pointer](d +% n.offset),
|
||||
genericDeepCopyAux(cast[pointer](d +% n.offset),
|
||||
cast[pointer](s +% n.offset), n.typ)
|
||||
of nkList:
|
||||
for i in 0..n.len-1:
|
||||
@@ -24,7 +24,7 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
|
||||
var m = selectBranch(src, n)
|
||||
# reset if different branches are in use; note different branches also
|
||||
# imply that's not self-assignment (``x = x``)!
|
||||
if m != dd and dd != nil:
|
||||
if m != dd and dd != nil:
|
||||
genericResetAux(dest, dd)
|
||||
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
|
||||
n.typ.size)
|
||||
@@ -103,16 +103,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
|
||||
else:
|
||||
let realType = x.typ
|
||||
let z = newObj(realType, realType.base.size)
|
||||
|
||||
|
||||
unsureAsgnRef(cast[PPointer](dest), z)
|
||||
x.typ = cast[PNimType](cast[int](z) or 1)
|
||||
genericDeepCopyAux(z, s2, realType.base)
|
||||
x.typ = realType
|
||||
else:
|
||||
let realType = mt
|
||||
let z = newObj(realType, realType.base.size)
|
||||
let z = newObj(realType, realType.base.size)
|
||||
unsureAsgnRef(cast[PPointer](dest), z)
|
||||
genericDeepCopyAux(z, s2, realType.base)
|
||||
genericDeepCopyAux(z, s2, realType.base)
|
||||
of tyPtr:
|
||||
# no cycle check here, but also not really required
|
||||
let s2 = cast[PPointer](src)[]
|
||||
|
||||
@@ -37,19 +37,19 @@ const
|
||||
|
||||
rcAlive = 0b00000 # object is reachable.
|
||||
# color *black* in the original paper
|
||||
|
||||
|
||||
rcCycleCandidate = 0b00001 # possible root of a cycle. *purple*
|
||||
|
||||
rcDecRefApplied = 0b00010 # the first dec-ref phase of the
|
||||
# collector was already applied to this
|
||||
# object. *gray*
|
||||
|
||||
|
||||
rcMaybeDead = 0b00011 # this object is a candidate for deletion
|
||||
# during the collect cycles algorithm.
|
||||
# *white*.
|
||||
|
||||
|
||||
rcReallyDead = 0b00100 # this is proved to be garbage
|
||||
|
||||
|
||||
rcRetiredBuffer = 0b00101 # this is a seq or string buffer that
|
||||
# was replaced by a resize operation.
|
||||
# see growObj for details
|
||||
@@ -80,14 +80,14 @@ const
|
||||
# The bit must also be set for new objects that are not rc1 and it must be
|
||||
# examined in the decref loop in collectCycles.
|
||||
# XXX: not implemented yet as tests didn't show any improvement from this
|
||||
|
||||
|
||||
MarkingSkipsAcyclicObjects = true
|
||||
# Acyclic objects can be safely ignored in the mark and scan phases,
|
||||
# Acyclic objects can be safely ignored in the mark and scan phases,
|
||||
# because they cannot contribute to the internal count.
|
||||
# XXX: if we generate specialized `markCyclic` and `markAcyclic`
|
||||
# procs we can further optimize this as there won't be need for any
|
||||
# checks in the code
|
||||
|
||||
|
||||
MinimumStackMarking = false
|
||||
# Try to scan only the user stack and ignore the part of the stack
|
||||
# belonging to the GC itself. see setStackTop for further info.
|
||||
@@ -110,9 +110,9 @@ type
|
||||
maxThreshold: int # max threshold that has been set
|
||||
maxStackSize: int # max stack size
|
||||
maxStackCells: int # max stack cells in ``decStack``
|
||||
cycleTableSize: int # max entries in cycle table
|
||||
cycleTableSize: int # max entries in cycle table
|
||||
maxPause: int64 # max measured GC pause in nanoseconds
|
||||
|
||||
|
||||
GcHeap {.final, pure.} = object # this contains the zero count and
|
||||
# non-zero count table
|
||||
stackBottom: pointer
|
||||
@@ -124,7 +124,7 @@ type
|
||||
tempStack: CellSeq # temporary stack for recursion elimination
|
||||
freeStack: CellSeq # objects ready to be freed
|
||||
recGcLock: int # prevent recursion via finalizers; no thread lock
|
||||
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
|
||||
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
|
||||
# cycle roots table that uses a cheap linear scan
|
||||
# to find only possitively dead objects.
|
||||
# One strategy is to perform it only for new objects
|
||||
@@ -143,11 +143,11 @@ var
|
||||
when not defined(useNimRtl):
|
||||
instantiateForRegion(gch.region)
|
||||
|
||||
template acquire(gch: GcHeap) =
|
||||
template acquire(gch: GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
AcquireSys(HeapLock)
|
||||
|
||||
template release(gch: GcHeap) =
|
||||
template release(gch: GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
releaseSys(HeapLock)
|
||||
|
||||
@@ -185,7 +185,7 @@ when debugGC:
|
||||
of rcRetiredBuffer: return "retired"
|
||||
of rcReallyDead: return "dead"
|
||||
else: return "unknown?"
|
||||
|
||||
|
||||
proc inCycleRootsStr(c: PCell): cstring =
|
||||
if c.isBitUp(rcInCycleRoots): result = "cycleroot"
|
||||
else: result = ""
|
||||
@@ -225,7 +225,7 @@ template setStackTop(gch) =
|
||||
template addCycleRoot(cycleRoots: var CellSeq, c: PCell) =
|
||||
if c.color != rcCycleCandidate:
|
||||
c.setColor rcCycleCandidate
|
||||
|
||||
|
||||
# the object may be buffered already. for example, consider:
|
||||
# decref; incref; decref
|
||||
if c.isBitDown(rcInCycleRoots):
|
||||
@@ -307,7 +307,7 @@ when traceGC:
|
||||
|
||||
let startLen = gch.tempStack.len
|
||||
c.forAllChildren waPush
|
||||
|
||||
|
||||
while startLen != gch.tempStack.len:
|
||||
dec gch.tempStack.len
|
||||
var c = gch.tempStack.d[gch.tempStack.len]
|
||||
@@ -331,7 +331,7 @@ when traceGC:
|
||||
if c.isBitUp(rcMarkBit) and not isMarked:
|
||||
writecell("cyclic cell", cell)
|
||||
cprintf "Weight %d\n", cell.computeCellWeight
|
||||
|
||||
|
||||
proc writeLeakage(onlyRoots: bool) =
|
||||
if onlyRoots:
|
||||
for c in elements(states[csAllocated]):
|
||||
@@ -356,7 +356,7 @@ template WithHeapLock(blk: stmt): stmt =
|
||||
blk
|
||||
when hasThreadSupport and hasSharedHeap: ReleaseSys(HeapLock)
|
||||
|
||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
WithHeapLock: addCycleRoot(gch.cycleRoots, c)
|
||||
|
||||
@@ -423,7 +423,7 @@ template doIncRef(cc: PCell,
|
||||
elif IncRefRemovesCandidates:
|
||||
c.setColor rcAlive
|
||||
# XXX: this is not really atomic enough!
|
||||
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerProc, inline.} = doIncRef(usrToCell(p))
|
||||
proc nimGCunref(p: pointer) {.compilerProc, inline.} = doDecRef(usrToCell(p))
|
||||
|
||||
@@ -449,7 +449,7 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
doAsgnRef(dest, src, LocalHeap, MaybeCyclic)
|
||||
|
||||
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc if it is known at compile time that no
|
||||
# the code generator calls this proc if it is known at compile time that no
|
||||
# cycle is possible.
|
||||
doAsgnRef(dest, src, LocalHeap, Acyclic)
|
||||
|
||||
@@ -509,7 +509,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) =
|
||||
if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
|
||||
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
|
||||
else:
|
||||
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
|
||||
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
|
||||
n.sons[i].typ, op)
|
||||
else:
|
||||
forAllSlotsAux(dest, n.sons[i], op)
|
||||
@@ -557,7 +557,7 @@ proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
|
||||
# we check the last 8 entries (cache line) for a slot that could be reused.
|
||||
# In 63% of all cases we succeed here! But we have to optimize the heck
|
||||
# out of this small linear search so that ``newObj`` is not slowed down.
|
||||
#
|
||||
#
|
||||
# Slots to try cache hit
|
||||
# 1 32%
|
||||
# 4 59%
|
||||
@@ -602,7 +602,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer
|
||||
acquire(gch)
|
||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||
sysAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
|
||||
|
||||
collectCT(gch)
|
||||
sysAssert(allocInv(gch.region), "rawNewObj after collect")
|
||||
|
||||
@@ -610,16 +610,16 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer
|
||||
sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc")
|
||||
|
||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
|
||||
|
||||
res.typ = typ
|
||||
|
||||
|
||||
when trackAllocationSource and not hasThreadSupport:
|
||||
if framePtr != nil and framePtr.prev != nil and framePtr.prev.prev != nil:
|
||||
res.filename = framePtr.prev.prev.filename
|
||||
res.line = framePtr.prev.prev.line
|
||||
else:
|
||||
res.filename = "nofile"
|
||||
|
||||
|
||||
if rc1:
|
||||
res.refcount = rcIncrement # refcount is 1
|
||||
else:
|
||||
@@ -631,9 +631,9 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer
|
||||
res.setBit(rcInCycleRoots)
|
||||
res.setColor rcCycleCandidate
|
||||
gch.cycleRoots.add res
|
||||
|
||||
|
||||
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||
|
||||
|
||||
when logGC: writeCell("new cell", res)
|
||||
gcTrace(res, csAllocated)
|
||||
release(gch)
|
||||
@@ -711,9 +711,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
|
||||
var elemSize = if ol.typ.kind != tyString: ol.typ.base.size
|
||||
else: 1
|
||||
|
||||
|
||||
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||
|
||||
|
||||
# XXX: This should happen outside
|
||||
# call user-defined move code
|
||||
# call user-defined default constructor
|
||||
@@ -723,24 +723,24 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||
|
||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
|
||||
sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
|
||||
|
||||
|
||||
when false:
|
||||
if ol.isBitUp(rcZct):
|
||||
var j = gch.zct.len-1
|
||||
var d = gch.zct.d
|
||||
while j >= 0:
|
||||
while j >= 0:
|
||||
if d[j] == ol:
|
||||
d[j] = res
|
||||
break
|
||||
dec(j)
|
||||
|
||||
|
||||
if ol.isBitUp(rcInCycleRoots):
|
||||
for i in 0 .. <gch.cycleRoots.len:
|
||||
if gch.cycleRoots.d[i] == ol:
|
||||
eraseAt(gch.cycleRoots, i)
|
||||
|
||||
freeCell(gch, ol)
|
||||
|
||||
|
||||
else:
|
||||
# the new buffer inherits the GC state of the old one
|
||||
if res.isBitUp(rcZct): gch.zct.add res
|
||||
@@ -787,12 +787,12 @@ proc doOperation(p: pointer, op: WalkOp) =
|
||||
var c: PCell = usrToCell(p)
|
||||
sysAssert(c != nil, "doOperation: 1")
|
||||
gch.tempStack.add c
|
||||
|
||||
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||
doOperation(d, WalkOp(op))
|
||||
|
||||
type
|
||||
RecursionType = enum
|
||||
RecursionType = enum
|
||||
FromChildren,
|
||||
FromRoot
|
||||
{.deprecated: [TRecursionType: RecursionType].}
|
||||
@@ -838,14 +838,14 @@ proc collectCycles(gch: var GcHeap) =
|
||||
let startLen = gch.tempStack.len
|
||||
cell.setColor rcAlive
|
||||
cell.forAllChildren waPush
|
||||
|
||||
|
||||
while startLen != gch.tempStack.len:
|
||||
dec gch.tempStack.len
|
||||
var c = gch.tempStack.d[gch.tempStack.len]
|
||||
if c.color != rcAlive:
|
||||
c.setColor rcAlive
|
||||
c.forAllChildren waPush
|
||||
|
||||
|
||||
template earlyMarkAlive(stackRoots) =
|
||||
# This marks all objects reachable from the stack as alive before any
|
||||
# of the other stages is executed. Such objects cannot be garbage and
|
||||
@@ -856,7 +856,7 @@ proc collectCycles(gch: var GcHeap) =
|
||||
earlyMarkAliveRec(c)
|
||||
|
||||
earlyMarkAlive(gch.decStack)
|
||||
|
||||
|
||||
when CollectCyclesStats:
|
||||
let tAfterEarlyMarkAlive = getTicks()
|
||||
|
||||
@@ -864,7 +864,7 @@ proc collectCycles(gch: var GcHeap) =
|
||||
let startLen = gch.tempStack.len
|
||||
cell.setColor rcDecRefApplied
|
||||
cell.forAllChildren waPush
|
||||
|
||||
|
||||
while startLen != gch.tempStack.len:
|
||||
dec gch.tempStack.len
|
||||
var c = gch.tempStack.d[gch.tempStack.len]
|
||||
@@ -876,7 +876,7 @@ proc collectCycles(gch: var GcHeap) =
|
||||
if c.color != rcDecRefApplied:
|
||||
c.setColor rcDecRefApplied
|
||||
c.forAllChildren waPush
|
||||
|
||||
|
||||
template markRoots(roots) =
|
||||
var i = 0
|
||||
while i < roots.len:
|
||||
@@ -885,34 +885,34 @@ proc collectCycles(gch: var GcHeap) =
|
||||
inc i
|
||||
else:
|
||||
roots.trimAt i
|
||||
|
||||
|
||||
markRoots(gch.cycleRoots)
|
||||
|
||||
|
||||
when CollectCyclesStats:
|
||||
let tAfterMark = getTicks()
|
||||
c_printf "COLLECT CYCLES %d: %d/%d\n", gcCollectionIdx, gch.cycleRoots.len, l0
|
||||
|
||||
|
||||
template recursiveMarkAlive(cell) =
|
||||
let startLen = gch.tempStack.len
|
||||
cell.setColor rcAlive
|
||||
cell.forAllChildren waPush
|
||||
|
||||
|
||||
while startLen != gch.tempStack.len:
|
||||
dec gch.tempStack.len
|
||||
var c = gch.tempStack.d[gch.tempStack.len]
|
||||
if ignoreObject(c): continue
|
||||
inc c.refcount, rcIncrement
|
||||
inc increfs
|
||||
|
||||
|
||||
if c.color != rcAlive:
|
||||
c.setColor rcAlive
|
||||
c.forAllChildren waPush
|
||||
|
||||
|
||||
template scanRoots(roots) =
|
||||
for i in 0 .. <roots.len:
|
||||
let startLen = gch.tempStack.len
|
||||
gch.tempStack.add roots.d[i]
|
||||
|
||||
|
||||
while startLen != gch.tempStack.len:
|
||||
dec gch.tempStack.len
|
||||
var c = gch.tempStack.d[gch.tempStack.len]
|
||||
@@ -928,9 +928,9 @@ proc collectCycles(gch: var GcHeap) =
|
||||
c.setColor rcMaybeDead
|
||||
inc maybedeads
|
||||
c.forAllChildren waPush
|
||||
|
||||
|
||||
scanRoots(gch.cycleRoots)
|
||||
|
||||
|
||||
when CollectCyclesStats:
|
||||
let tAfterScan = getTicks()
|
||||
|
||||
@@ -941,7 +941,7 @@ proc collectCycles(gch: var GcHeap) =
|
||||
|
||||
let startLen = gch.tempStack.len
|
||||
gch.tempStack.add c
|
||||
|
||||
|
||||
while startLen != gch.tempStack.len:
|
||||
dec gch.tempStack.len
|
||||
var c = gch.tempStack.d[gch.tempStack.len]
|
||||
@@ -965,7 +965,7 @@ proc collectCycles(gch: var GcHeap) =
|
||||
freeCell(gch, gch.freeStack.d[i])
|
||||
|
||||
collectDead(gch.cycleRoots)
|
||||
|
||||
|
||||
when CollectCyclesStats:
|
||||
let tFinal = getTicks()
|
||||
cprintf "times:\n early mark alive: %d ms\n mark: %d ms\n scan: %d ms\n collect: %d ms\n decrefs: %d\n increfs: %d\n marked dead: %d\n collected: %d\n",
|
||||
@@ -986,7 +986,7 @@ proc collectCycles(gch: var GcHeap) =
|
||||
|
||||
when MarkingSkipsAcyclicObjects:
|
||||
# Collect the acyclic objects that became unreachable due to collected
|
||||
# cyclic objects.
|
||||
# cyclic objects.
|
||||
discard collectZCT(gch)
|
||||
# collectZCT may add new cycle candidates and we may decide to loop here
|
||||
# if gch.cycleRoots.len > 0: repeat
|
||||
@@ -1030,12 +1030,12 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
||||
add(gch.decStack, cell)
|
||||
sysAssert(allocInv(gch.region), "gcMark end")
|
||||
|
||||
proc markThreadStacks(gch: var GcHeap) =
|
||||
proc markThreadStacks(gch: var GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
{.error: "not fully implemented".}
|
||||
var it = threadList
|
||||
while it != nil:
|
||||
# mark registers:
|
||||
# mark registers:
|
||||
for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
|
||||
var sp = cast[ByteAddress](it.stackBottom)
|
||||
var max = cast[ByteAddress](it.stackTop)
|
||||
@@ -1121,7 +1121,7 @@ elif stackIncreases:
|
||||
var b = cast[ByteAddress](stackTop)
|
||||
var x = cast[ByteAddress](p)
|
||||
result = a <=% x and x <=% b
|
||||
|
||||
|
||||
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
|
||||
var registers: C_JmpBuf
|
||||
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
||||
@@ -1156,7 +1156,7 @@ else:
|
||||
# mark the registers
|
||||
var jmpbufPtr = cast[ByteAddress](addr(registers))
|
||||
var jmpbufEnd = jmpbufPtr +% jmpbufSize
|
||||
|
||||
|
||||
while jmpbufPtr <=% jmpbufEnd:
|
||||
gcMark(gch, cast[PPointer](jmpbufPtr)[])
|
||||
jmpbufPtr = jmpbufPtr +% sizeof(pointer)
|
||||
@@ -1218,18 +1218,18 @@ proc releaseCell(gch: var GcHeap, cell: PCell) =
|
||||
proc collectZCT(gch: var GcHeap): bool =
|
||||
const workPackage = 100
|
||||
var L = addr(gch.zct.len)
|
||||
|
||||
|
||||
when withRealtime:
|
||||
var steps = workPackage
|
||||
var t0: Ticks
|
||||
if gch.maxPause > 0: t0 = getticks()
|
||||
|
||||
|
||||
while L[] > 0:
|
||||
var c = gch.zct.d[0]
|
||||
sysAssert c.isBitUp(rcZct), "collectZCT: rcZct missing!"
|
||||
sysAssert(isAllocatedPtr(gch.region, c), "collectZCT: isAllocatedPtr")
|
||||
|
||||
# remove from ZCT:
|
||||
|
||||
# remove from ZCT:
|
||||
c.clearBit(rcZct)
|
||||
gch.zct.d[0] = gch.zct.d[L[] - 1]
|
||||
dec(L[])
|
||||
@@ -1237,7 +1237,7 @@ proc collectZCT(gch: var GcHeap): bool =
|
||||
if c.refcount <% rcIncrement:
|
||||
# It may have a RC > 0, if it is in the hardware stack or
|
||||
# it has not been removed yet from the ZCT. This is because
|
||||
# ``incref`` does not bother to remove the cell from the ZCT
|
||||
# ``incref`` does not bother to remove the cell from the ZCT
|
||||
# as this might be too slow.
|
||||
# In any case, it should be removed from the ZCT. But not
|
||||
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
|
||||
@@ -1252,7 +1252,7 @@ proc collectZCT(gch: var GcHeap): bool =
|
||||
steps = workPackage
|
||||
if gch.maxPause > 0:
|
||||
let duration = getticks() - t0
|
||||
# the GC's measuring is not accurate and needs some cleanup actions
|
||||
# the GC's measuring is not accurate and needs some cleanup actions
|
||||
# (stack unmarking), so subtract some short amount of time in to
|
||||
# order to miss deadlines less often:
|
||||
if duration >= gch.maxPause - 50_000:
|
||||
@@ -1269,7 +1269,7 @@ proc unmarkStackAndRegisters(gch: var GcHeap) =
|
||||
# XXX: just call doDecRef?
|
||||
var c = d[i]
|
||||
sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
|
||||
|
||||
|
||||
if c.color == rcRetiredBuffer:
|
||||
continue
|
||||
|
||||
@@ -1278,7 +1278,7 @@ proc unmarkStackAndRegisters(gch: var GcHeap) =
|
||||
# the object survived only because of a stack reference
|
||||
# it still doesn't have heap references
|
||||
addZCT(gch.zct, c)
|
||||
|
||||
|
||||
if canbeCycleRoot(c):
|
||||
# any cyclic object reachable from the stack can be turned into
|
||||
# a leak if it's orphaned through the stack reference
|
||||
@@ -1293,7 +1293,7 @@ proc collectCTBody(gch: var GcHeap) =
|
||||
let t0 = getticks()
|
||||
when debugGC: inc gcCollectionIdx
|
||||
sysAssert(allocInv(gch.region), "collectCT: begin")
|
||||
|
||||
|
||||
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
|
||||
sysAssert(gch.decStack.len == 0, "collectCT")
|
||||
prepareForInteriorPointerChecking(gch.region)
|
||||
@@ -1312,7 +1312,7 @@ proc collectCTBody(gch: var GcHeap) =
|
||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||
unmarkStackAndRegisters(gch)
|
||||
sysAssert(allocInv(gch.region), "collectCT: end")
|
||||
|
||||
|
||||
when withRealtime:
|
||||
let duration = getticks() - t0
|
||||
gch.stat.maxPause = max(gch.stat.maxPause, duration)
|
||||
@@ -1322,7 +1322,7 @@ proc collectCTBody(gch: var GcHeap) =
|
||||
|
||||
proc collectCT(gch: var GcHeap) =
|
||||
if (gch.zct.len >= ZctThreshold or (cycleGC and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
|
||||
gch.recGcLock == 0:
|
||||
collectCTBody(gch)
|
||||
|
||||
@@ -1337,7 +1337,7 @@ when withRealtime:
|
||||
acquire(gch)
|
||||
gch.maxPause = us.toNano
|
||||
if (gch.zct.len >= ZctThreshold or (cycleGC and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
|
||||
strongAdvice:
|
||||
collectCTBody(gch)
|
||||
release(gch)
|
||||
@@ -1345,13 +1345,13 @@ when withRealtime:
|
||||
proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice)
|
||||
|
||||
when not defined(useNimRtl):
|
||||
proc GC_disable() =
|
||||
proc GC_disable() =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
discard atomicInc(gch.recGcLock, 1)
|
||||
else:
|
||||
inc(gch.recGcLock)
|
||||
proc GC_enable() =
|
||||
if gch.recGcLock > 0:
|
||||
if gch.recGcLock > 0:
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
discard atomicDec(gch.recGcLock, 1)
|
||||
else:
|
||||
|
||||
@@ -197,7 +197,7 @@ else:
|
||||
var x = cast[ByteAddress](p)
|
||||
if a <=% x and x <=% b:
|
||||
return true
|
||||
|
||||
|
||||
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
||||
# We use a jmp_buf buffer that is in the C stack.
|
||||
# Used to traverse the stack and registers assuming
|
||||
@@ -207,7 +207,7 @@ else:
|
||||
getRegisters(registers)
|
||||
for i in registers.low .. registers.high:
|
||||
gcMark(gch, cast[PPointer](registers[i]))
|
||||
|
||||
|
||||
for stack in items(gch.stack):
|
||||
stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts))
|
||||
var max = cast[ByteAddress](stack.starts)
|
||||
|
||||
@@ -20,21 +20,21 @@ when not defined(nimNewShared):
|
||||
{.pragma: gcsafe.}
|
||||
|
||||
when defined(createNimRtl):
|
||||
when defined(useNimRtl):
|
||||
when defined(useNimRtl):
|
||||
{.error: "Cannot create and use nimrtl at the same time!".}
|
||||
elif appType != "lib":
|
||||
{.error: "nimrtl must be built as a library!".}
|
||||
|
||||
when defined(createNimRtl):
|
||||
when defined(createNimRtl):
|
||||
{.pragma: rtl, exportc: "nimrtl_$1", dynlib, gcsafe.}
|
||||
{.pragma: inl.}
|
||||
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
|
||||
elif defined(useNimRtl):
|
||||
when defined(windows):
|
||||
when defined(windows):
|
||||
const nimrtl* = "nimrtl.dll"
|
||||
elif defined(macosx):
|
||||
const nimrtl* = "libnimrtl.dylib"
|
||||
else:
|
||||
else:
|
||||
const nimrtl* = "libnimrtl.so"
|
||||
{.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.}
|
||||
{.pragma: inl.}
|
||||
|
||||
@@ -40,7 +40,7 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
|
||||
while it != nil:
|
||||
inc(total)
|
||||
it = it.prev
|
||||
for j in 1..total-i-(firstCalls-1):
|
||||
for j in 1..total-i-(firstCalls-1):
|
||||
if b != nil: b = b.prev
|
||||
if total != i:
|
||||
st[i] = "..."
|
||||
|
||||
@@ -23,7 +23,7 @@ when defined(Windows):
|
||||
SysCond = Handle
|
||||
|
||||
{.deprecated: [THandle: Handle, TSysLock: SysLock, TSysCond: SysCond].}
|
||||
|
||||
|
||||
proc initSysLock(L: var SysLock) {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "InitializeCriticalSection".}
|
||||
## Initializes the lock `L`.
|
||||
@@ -31,14 +31,14 @@ when defined(Windows):
|
||||
proc tryAcquireSysAux(L: var SysLock): int32 {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "TryEnterCriticalSection".}
|
||||
## Tries to acquire the lock `L`.
|
||||
|
||||
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
|
||||
|
||||
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
|
||||
result = tryAcquireSysAux(L) != 0'i32
|
||||
|
||||
proc acquireSys(L: var SysLock) {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "EnterCriticalSection".}
|
||||
## Acquires the lock `L`.
|
||||
|
||||
|
||||
proc releaseSys(L: var SysLock) {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "LeaveCriticalSection".}
|
||||
## Releases the lock `L`.
|
||||
@@ -46,11 +46,11 @@ when defined(Windows):
|
||||
proc deinitSys(L: var SysLock) {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "DeleteCriticalSection".}
|
||||
|
||||
proc createEvent(lpEventAttributes: pointer,
|
||||
proc createEvent(lpEventAttributes: pointer,
|
||||
bManualReset, bInitialState: int32,
|
||||
lpName: cstring): SysCond {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "CreateEventA".}
|
||||
|
||||
|
||||
proc closeHandle(hObject: Handle) {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "CloseHandle".}
|
||||
proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {.
|
||||
@@ -58,7 +58,7 @@ when defined(Windows):
|
||||
|
||||
proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect,
|
||||
dynlib: "kernel32", importc: "SetEvent".}
|
||||
|
||||
|
||||
proc initSysCond(cond: var SysCond) {.inline.} =
|
||||
cond = createEvent(nil, 0'i32, 0'i32, nil)
|
||||
proc deinitSysCond(cond: var SysCond) {.inline.} =
|
||||
@@ -86,7 +86,7 @@ else:
|
||||
proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
|
||||
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
|
||||
|
||||
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
|
||||
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
|
||||
result = tryAcquireSysAux(L) == 0'i32
|
||||
|
||||
proc releaseSys(L: var SysLock) {.noSideEffect,
|
||||
@@ -100,7 +100,7 @@ else:
|
||||
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
|
||||
proc signalSysCond(cond: var SysCond) {.
|
||||
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
|
||||
|
||||
|
||||
proc deinitSysCond(cond: var SysCond) {.noSideEffect,
|
||||
importc: "pthread_cond_destroy", header: "<pthread.h>".}
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## Implements Nim's 'spawn'.
|
||||
|
||||
when not declared(NimString):
|
||||
when not declared(NimString):
|
||||
{.error: "You must not import this module explicitly".}
|
||||
|
||||
{.push stackTrace:off.}
|
||||
|
||||
@@ -34,8 +34,8 @@ when defined(windows):
|
||||
|
||||
elif defined(macosx):
|
||||
type
|
||||
MachTimebaseInfoData {.pure, final,
|
||||
importc: "mach_timebase_info_data_t",
|
||||
MachTimebaseInfoData {.pure, final,
|
||||
importc: "mach_timebase_info_data_t",
|
||||
header: "<mach/mach_time.h>".} = object
|
||||
numer, denom: int32
|
||||
{.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
|
||||
@@ -46,10 +46,10 @@ elif defined(macosx):
|
||||
|
||||
proc getTicks(): Ticks {.inline.} =
|
||||
result = Ticks(mach_absolute_time())
|
||||
|
||||
|
||||
var timeBaseInfo: MachTimebaseInfoData
|
||||
mach_timebase_info(timeBaseInfo)
|
||||
|
||||
|
||||
proc `-`(a, b: Ticks): Nanos =
|
||||
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
|
||||
|
||||
@@ -57,10 +57,10 @@ elif defined(posixRealtime):
|
||||
type
|
||||
Clockid {.importc: "clockid_t", header: "<time.h>", final.} = object
|
||||
|
||||
TimeSpec {.importc: "struct timespec", header: "<time.h>",
|
||||
TimeSpec {.importc: "struct timespec", header: "<time.h>",
|
||||
final, pure.} = object ## struct timespec
|
||||
tv_sec: int ## Seconds.
|
||||
tv_nsec: int ## Nanoseconds.
|
||||
tv_sec: int ## Seconds.
|
||||
tv_nsec: int ## Nanoseconds.
|
||||
{.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
|
||||
|
||||
var
|
||||
@@ -77,12 +77,12 @@ elif defined(posixRealtime):
|
||||
proc `-`(a, b: Ticks): Nanos {.borrow.}
|
||||
|
||||
else:
|
||||
# fallback Posix implementation:
|
||||
# fallback Posix implementation:
|
||||
type
|
||||
Timeval {.importc: "struct timeval", header: "<sys/select.h>",
|
||||
Timeval {.importc: "struct timeval", header: "<sys/select.h>",
|
||||
final, pure.} = object ## struct timeval
|
||||
tv_sec: int ## Seconds.
|
||||
tv_usec: int ## Microseconds.
|
||||
tv_sec: int ## Seconds.
|
||||
tv_usec: int ## Microseconds.
|
||||
{.deprecated: [Ttimeval: Timeval].}
|
||||
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
|
||||
importc: "gettimeofday", header: "<sys/time.h>".}
|
||||
@@ -90,7 +90,7 @@ else:
|
||||
proc getTicks(): Ticks =
|
||||
var t: Timeval
|
||||
posix_gettimeofday(t)
|
||||
result = Ticks(int64(t.tv_sec) * 1000_000_000'i64 +
|
||||
result = Ticks(int64(t.tv_sec) * 1000_000_000'i64 +
|
||||
int64(t.tv_usec) * 1000'i64)
|
||||
|
||||
proc `-`(a, b: Ticks): Nanos {.borrow.}
|
||||
|
||||
@@ -124,7 +124,7 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
|
||||
if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
|
||||
# If the 16 bits following the high surrogate are in the source buffer...
|
||||
let ch2 = int(cast[uint16](w[i]))
|
||||
|
||||
|
||||
# If it's a low surrogate, convert to UTF32:
|
||||
if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
|
||||
ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
|
||||
@@ -135,7 +135,7 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
|
||||
elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
|
||||
#invalid UTF-16
|
||||
ch = replacement
|
||||
|
||||
|
||||
if ch < 0x80:
|
||||
result.add chr(ch)
|
||||
elif ch < 0x800:
|
||||
@@ -155,6 +155,6 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
|
||||
result.add chr(0xFFFD shr 12 or 0b1110_0000)
|
||||
result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
|
||||
result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
|
||||
|
||||
|
||||
proc `$`*(s: WideCString): string =
|
||||
result = s $ 80
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
## This module is a low level wrapper for `libsvm`:idx:.
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
const
|
||||
const
|
||||
LIBSVM_VERSION* = 312
|
||||
|
||||
|
||||
when defined(windows):
|
||||
const svmdll* = "libsvm.dll"
|
||||
elif defined(macosx):
|
||||
@@ -20,97 +20,97 @@ elif defined(macosx):
|
||||
else:
|
||||
const svmdll* = "libsvm.so"
|
||||
|
||||
type
|
||||
Node*{.pure, final.} = object
|
||||
type
|
||||
Node*{.pure, final.} = object
|
||||
index*: cint
|
||||
value*: cdouble
|
||||
|
||||
Problem*{.pure, final.} = object
|
||||
Problem*{.pure, final.} = object
|
||||
L*: cint
|
||||
y*: ptr cdouble
|
||||
x*: ptr ptr Node
|
||||
|
||||
Type*{.size: sizeof(cint).} = enum
|
||||
Type*{.size: sizeof(cint).} = enum
|
||||
C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR
|
||||
|
||||
KernelType*{.size: sizeof(cint).} = enum
|
||||
|
||||
KernelType*{.size: sizeof(cint).} = enum
|
||||
LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED
|
||||
|
||||
Parameter*{.pure, final.} = object
|
||||
|
||||
Parameter*{.pure, final.} = object
|
||||
typ*: Type
|
||||
kernelType*: KernelType
|
||||
degree*: cint # for poly
|
||||
gamma*: cdouble # for poly/rbf/sigmoid
|
||||
coef0*: cdouble # for poly/sigmoid
|
||||
# these are for training only
|
||||
cache_size*: cdouble # in MB
|
||||
eps*: cdouble # stopping criteria
|
||||
C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR
|
||||
nr_weight*: cint # for C_SVC
|
||||
weight_label*: ptr cint # for C_SVC
|
||||
weight*: ptr cdouble # for C_SVC
|
||||
nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR
|
||||
p*: cdouble # for EPSILON_SVR
|
||||
shrinking*: cint # use the shrinking heuristics
|
||||
probability*: cint # do probability estimates
|
||||
degree*: cint # for poly
|
||||
gamma*: cdouble # for poly/rbf/sigmoid
|
||||
coef0*: cdouble # for poly/sigmoid
|
||||
# these are for training only
|
||||
cache_size*: cdouble # in MB
|
||||
eps*: cdouble # stopping criteria
|
||||
C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR
|
||||
nr_weight*: cint # for C_SVC
|
||||
weight_label*: ptr cint # for C_SVC
|
||||
weight*: ptr cdouble # for C_SVC
|
||||
nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR
|
||||
p*: cdouble # for EPSILON_SVR
|
||||
shrinking*: cint # use the shrinking heuristics
|
||||
probability*: cint # do probability estimates
|
||||
{.deprecated: [Tnode: Node, Tproblem: Problem, Ttype: Type,
|
||||
TKernelType: KernelType, Tparameter: Parameter].}
|
||||
|
||||
#
|
||||
# svm_model
|
||||
#
|
||||
#
|
||||
|
||||
type
|
||||
Model*{.pure, final.} = object
|
||||
param*: Parameter # parameter
|
||||
nr_class*: cint # number of classes, = 2 in regression/one class svm
|
||||
L*: cint # total #SV
|
||||
type
|
||||
Model*{.pure, final.} = object
|
||||
param*: Parameter # parameter
|
||||
nr_class*: cint # number of classes, = 2 in regression/one class svm
|
||||
L*: cint # total #SV
|
||||
SV*: ptr ptr Node # SVs (SV[l])
|
||||
sv_coef*: ptr ptr cdouble # coefficients for SVs in decision functions (sv_coef[k-1][l])
|
||||
rho*: ptr cdouble # constants in decision functions (rho[k*(k-1)/2])
|
||||
probA*: ptr cdouble # pariwise probability information
|
||||
probB*: ptr cdouble # for classification only
|
||||
label*: ptr cint # label of each class (label[k])
|
||||
nSV*: ptr cint # number of SVs for each class (nSV[k])
|
||||
# nSV[0] + nSV[1] + ... + nSV[k-1] = l
|
||||
# XXX
|
||||
sv_coef*: ptr ptr cdouble # coefficients for SVs in decision functions (sv_coef[k-1][l])
|
||||
rho*: ptr cdouble # constants in decision functions (rho[k*(k-1)/2])
|
||||
probA*: ptr cdouble # pariwise probability information
|
||||
probB*: ptr cdouble # for classification only
|
||||
label*: ptr cint # label of each class (label[k])
|
||||
nSV*: ptr cint # number of SVs for each class (nSV[k])
|
||||
# nSV[0] + nSV[1] + ... + nSV[k-1] = l
|
||||
# XXX
|
||||
free_sv*: cint # 1 if svm_model is created by svm_load_model
|
||||
# 0 if svm_model is created by svm_train
|
||||
{.deprecated: [TModel: Model].}
|
||||
|
||||
proc train*(prob: ptr Problem, param: ptr Parameter): ptr Model{.cdecl,
|
||||
proc train*(prob: ptr Problem, param: ptr Parameter): ptr Model{.cdecl,
|
||||
importc: "svm_train", dynlib: svmdll.}
|
||||
proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint,
|
||||
target: ptr cdouble){.cdecl,
|
||||
proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint,
|
||||
target: ptr cdouble){.cdecl,
|
||||
importc: "svm_cross_validation", dynlib: svmdll.}
|
||||
proc save_model*(model_file_name: cstring, model: ptr Model): cint{.cdecl,
|
||||
proc save_model*(model_file_name: cstring, model: ptr Model): cint{.cdecl,
|
||||
importc: "svm_save_model", dynlib: svmdll.}
|
||||
proc load_model*(model_file_name: cstring): ptr Model{.cdecl,
|
||||
proc load_model*(model_file_name: cstring): ptr Model{.cdecl,
|
||||
importc: "svm_load_model", dynlib: svmdll.}
|
||||
proc get_svm_type*(model: ptr Model): cint{.cdecl, importc: "svm_get_svm_type",
|
||||
proc get_svm_type*(model: ptr Model): cint{.cdecl, importc: "svm_get_svm_type",
|
||||
dynlib: svmdll.}
|
||||
proc get_nr_class*(model: ptr Model): cint{.cdecl, importc: "svm_get_nr_class",
|
||||
proc get_nr_class*(model: ptr Model): cint{.cdecl, importc: "svm_get_nr_class",
|
||||
dynlib: svmdll.}
|
||||
proc get_labels*(model: ptr Model, label: ptr cint){.cdecl,
|
||||
proc get_labels*(model: ptr Model, label: ptr cint){.cdecl,
|
||||
importc: "svm_get_labels", dynlib: svmdll.}
|
||||
proc get_svr_probability*(model: ptr Model): cdouble{.cdecl,
|
||||
proc get_svr_probability*(model: ptr Model): cdouble{.cdecl,
|
||||
importc: "svm_get_svr_probability", dynlib: svmdll.}
|
||||
proc predict_values*(model: ptr Model, x: ptr Node, dec_values: ptr cdouble): cdouble{.
|
||||
cdecl, importc: "svm_predict_values", dynlib: svmdll.}
|
||||
proc predict*(model: ptr Model, x: ptr Node): cdouble{.cdecl,
|
||||
proc predict*(model: ptr Model, x: ptr Node): cdouble{.cdecl,
|
||||
importc: "svm_predict", dynlib: svmdll.}
|
||||
proc predict_probability*(model: ptr Model, x: ptr Node,
|
||||
prob_estimates: ptr cdouble): cdouble{.cdecl,
|
||||
proc predict_probability*(model: ptr Model, x: ptr Node,
|
||||
prob_estimates: ptr cdouble): cdouble{.cdecl,
|
||||
importc: "svm_predict_probability", dynlib: svmdll.}
|
||||
proc free_model_content*(model_ptr: ptr Model){.cdecl,
|
||||
proc free_model_content*(model_ptr: ptr Model){.cdecl,
|
||||
importc: "svm_free_model_content", dynlib: svmdll.}
|
||||
proc free_and_destroy_model*(model_ptr_ptr: ptr ptr Model){.cdecl,
|
||||
proc free_and_destroy_model*(model_ptr_ptr: ptr ptr Model){.cdecl,
|
||||
importc: "svm_free_and_destroy_model", dynlib: svmdll.}
|
||||
proc destroy_param*(param: ptr Parameter){.cdecl, importc: "svm_destroy_param",
|
||||
proc destroy_param*(param: ptr Parameter){.cdecl, importc: "svm_destroy_param",
|
||||
dynlib: svmdll.}
|
||||
proc check_parameter*(prob: ptr Problem, param: ptr Parameter): cstring{.
|
||||
cdecl, importc: "svm_check_parameter", dynlib: svmdll.}
|
||||
proc check_probability_model*(model: ptr Model): cint{.cdecl,
|
||||
proc check_probability_model*(model: ptr Model): cint{.cdecl,
|
||||
importc: "svm_check_probability_model", dynlib: svmdll.}
|
||||
|
||||
proc set_print_string_function*(print_func: proc (arg: cstring) {.cdecl.}){.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,17 +8,17 @@
|
||||
#
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
when defined(windows):
|
||||
const
|
||||
when defined(windows):
|
||||
const
|
||||
Lib = "sqlite3.dll"
|
||||
elif defined(macosx):
|
||||
const
|
||||
elif defined(macosx):
|
||||
const
|
||||
Lib = "libsqlite3(|.0).dylib"
|
||||
else:
|
||||
const
|
||||
else:
|
||||
const
|
||||
Lib = "libsqlite3.so(|.0)"
|
||||
|
||||
const
|
||||
|
||||
const
|
||||
SQLITE_INTEGER* = 1
|
||||
SQLITE_FLOAT* = 2
|
||||
SQLITE_BLOB* = 4
|
||||
@@ -26,38 +26,38 @@ const
|
||||
SQLITE_TEXT* = 3
|
||||
SQLITE_UTF8* = 1
|
||||
SQLITE_UTF16LE* = 2
|
||||
SQLITE_UTF16BE* = 3 # Use native byte order
|
||||
SQLITE_UTF16* = 4 # sqlite3_create_function only
|
||||
SQLITE_UTF16BE* = 3 # Use native byte order
|
||||
SQLITE_UTF16* = 4 # sqlite3_create_function only
|
||||
SQLITE_ANY* = 5 #sqlite_exec return values
|
||||
SQLITE_OK* = 0
|
||||
SQLITE_ERROR* = 1 # SQL error or missing database
|
||||
SQLITE_INTERNAL* = 2 # An internal logic error in SQLite
|
||||
SQLITE_PERM* = 3 # Access permission denied
|
||||
SQLITE_ABORT* = 4 # Callback routine requested an abort
|
||||
SQLITE_BUSY* = 5 # The database file is locked
|
||||
SQLITE_LOCKED* = 6 # A table in the database is locked
|
||||
SQLITE_NOMEM* = 7 # A malloc() failed
|
||||
SQLITE_READONLY* = 8 # Attempt to write a readonly database
|
||||
SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt()
|
||||
SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred
|
||||
SQLITE_CORRUPT* = 11 # The database disk image is malformed
|
||||
SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found
|
||||
SQLITE_FULL* = 13 # Insertion failed because database is full
|
||||
SQLITE_CANTOPEN* = 14 # Unable to open the database file
|
||||
SQLITE_PROTOCOL* = 15 # Database lock protocol error
|
||||
SQLITE_EMPTY* = 16 # Database is empty
|
||||
SQLITE_SCHEMA* = 17 # The database schema changed
|
||||
SQLITE_TOOBIG* = 18 # Too much data for one row of a table
|
||||
SQLITE_CONSTRAINT* = 19 # Abort due to contraint violation
|
||||
SQLITE_MISMATCH* = 20 # Data type mismatch
|
||||
SQLITE_MISUSE* = 21 # Library used incorrectly
|
||||
SQLITE_NOLFS* = 22 # Uses OS features not supported on host
|
||||
SQLITE_AUTH* = 23 # Authorization denied
|
||||
SQLITE_FORMAT* = 24 # Auxiliary database format error
|
||||
SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range
|
||||
SQLITE_NOTADB* = 26 # File opened that is not a database file
|
||||
SQLITE_ROW* = 100 # sqlite3_step() has another row ready
|
||||
SQLITE_DONE* = 101 # sqlite3_step() has finished executing
|
||||
SQLITE_ERROR* = 1 # SQL error or missing database
|
||||
SQLITE_INTERNAL* = 2 # An internal logic error in SQLite
|
||||
SQLITE_PERM* = 3 # Access permission denied
|
||||
SQLITE_ABORT* = 4 # Callback routine requested an abort
|
||||
SQLITE_BUSY* = 5 # The database file is locked
|
||||
SQLITE_LOCKED* = 6 # A table in the database is locked
|
||||
SQLITE_NOMEM* = 7 # A malloc() failed
|
||||
SQLITE_READONLY* = 8 # Attempt to write a readonly database
|
||||
SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt()
|
||||
SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred
|
||||
SQLITE_CORRUPT* = 11 # The database disk image is malformed
|
||||
SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found
|
||||
SQLITE_FULL* = 13 # Insertion failed because database is full
|
||||
SQLITE_CANTOPEN* = 14 # Unable to open the database file
|
||||
SQLITE_PROTOCOL* = 15 # Database lock protocol error
|
||||
SQLITE_EMPTY* = 16 # Database is empty
|
||||
SQLITE_SCHEMA* = 17 # The database schema changed
|
||||
SQLITE_TOOBIG* = 18 # Too much data for one row of a table
|
||||
SQLITE_CONSTRAINT* = 19 # Abort due to contraint violation
|
||||
SQLITE_MISMATCH* = 20 # Data type mismatch
|
||||
SQLITE_MISUSE* = 21 # Library used incorrectly
|
||||
SQLITE_NOLFS* = 22 # Uses OS features not supported on host
|
||||
SQLITE_AUTH* = 23 # Authorization denied
|
||||
SQLITE_FORMAT* = 24 # Auxiliary database format error
|
||||
SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range
|
||||
SQLITE_NOTADB* = 26 # File opened that is not a database file
|
||||
SQLITE_ROW* = 100 # sqlite3_step() has another row ready
|
||||
SQLITE_DONE* = 101 # sqlite3_step() has finished executing
|
||||
SQLITE_COPY* = 0
|
||||
SQLITE_CREATE_INDEX* = 1
|
||||
SQLITE_CREATE_TABLE* = 2
|
||||
@@ -87,39 +87,39 @@ const
|
||||
SQLITE_ALTER_TABLE* = 26
|
||||
SQLITE_REINDEX* = 27
|
||||
SQLITE_DENY* = 1
|
||||
SQLITE_IGNORE* = 2 # Original from sqlite3.h:
|
||||
SQLITE_IGNORE* = 2 # Original from sqlite3.h:
|
||||
#define SQLITE_STATIC ((void(*)(void *))0)
|
||||
#define SQLITE_TRANSIENT ((void(*)(void *))-1)
|
||||
SQLITE_DETERMINISTIC* = 0x800
|
||||
|
||||
const
|
||||
const
|
||||
SQLITE_STATIC* = nil
|
||||
SQLITE_TRANSIENT* = cast[pointer](- 1)
|
||||
|
||||
type
|
||||
Sqlite3 {.pure, final.} = object
|
||||
type
|
||||
Sqlite3 {.pure, final.} = object
|
||||
PSqlite3* = ptr Sqlite3
|
||||
PPSqlite3* = ptr PSqlite3
|
||||
Context{.pure, final.} = object
|
||||
Context{.pure, final.} = object
|
||||
Pcontext* = ptr Context
|
||||
Tstmt{.pure, final.} = object
|
||||
Tstmt{.pure, final.} = object
|
||||
Pstmt* = ptr Tstmt
|
||||
Value{.pure, final.} = object
|
||||
Value{.pure, final.} = object
|
||||
Pvalue* = ptr Value
|
||||
PValueArg* = array[0..127, Pvalue]
|
||||
|
||||
Callback* = proc (para1: pointer, para2: int32, para3,
|
||||
|
||||
Callback* = proc (para1: pointer, para2: int32, para3,
|
||||
para4: cstringArray): int32{.cdecl.}
|
||||
Tbind_destructor_func* = proc (para1: pointer){.cdecl.}
|
||||
Create_function_step_func* = proc (para1: Pcontext, para2: int32,
|
||||
Create_function_step_func* = proc (para1: Pcontext, para2: int32,
|
||||
para3: PValueArg){.cdecl.}
|
||||
Create_function_func_func* = proc (para1: Pcontext, para2: int32,
|
||||
Create_function_func_func* = proc (para1: Pcontext, para2: int32,
|
||||
para3: PValueArg){.cdecl.}
|
||||
Create_function_final_func* = proc (para1: Pcontext){.cdecl.}
|
||||
Result_func* = proc (para1: pointer){.cdecl.}
|
||||
Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer,
|
||||
Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer,
|
||||
para4: int32, para5: pointer): int32{.cdecl.}
|
||||
Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32,
|
||||
Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32,
|
||||
para4: cstring){.cdecl.}
|
||||
{.deprecated: [TSqlite3: Sqlite3, TContext: Context, Tvalue: Value,
|
||||
Tcallback: Callback, Tcreate_function_step_func: Create_function_step_func,
|
||||
@@ -129,220 +129,220 @@ type
|
||||
Tcollation_needed_func: Collation_needed_func].}
|
||||
|
||||
proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".}
|
||||
proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
|
||||
errmsg: var cstring): int32{.cdecl, dynlib: Lib,
|
||||
proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
|
||||
errmsg: var cstring): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_exec".}
|
||||
proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, dynlib: Lib,
|
||||
proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_last_insert_rowid".}
|
||||
proc changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_changes".}
|
||||
proc total_changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib,
|
||||
proc total_changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_total_changes".}
|
||||
proc interrupt*(para1: PSqlite3){.cdecl, dynlib: Lib, importc: "sqlite3_interrupt".}
|
||||
proc complete*(sql: cstring): int32{.cdecl, dynlib: Lib,
|
||||
proc complete*(sql: cstring): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_complete".}
|
||||
proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib,
|
||||
proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_complete16".}
|
||||
proc busy_handler*(para1: PSqlite3,
|
||||
para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
|
||||
para3: pointer): int32{.cdecl, dynlib: Lib,
|
||||
proc busy_handler*(para1: PSqlite3,
|
||||
para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
|
||||
para3: pointer): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_busy_handler".}
|
||||
proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_busy_timeout".}
|
||||
proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
|
||||
nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
|
||||
proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
|
||||
nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_get_table".}
|
||||
proc free_table*(result: cstringArray){.cdecl, dynlib: Lib,
|
||||
proc free_table*(result: cstringArray){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_free_table".}
|
||||
# Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf
|
||||
# function sqlite3_mprintf(_para1:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_mprintf';
|
||||
proc mprintf*(para1: cstring): cstring{.cdecl, varargs, dynlib: Lib,
|
||||
proc mprintf*(para1: cstring): cstring{.cdecl, varargs, dynlib: Lib,
|
||||
importc: "sqlite3_mprintf".}
|
||||
#function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf';
|
||||
proc free*(z: cstring){.cdecl, dynlib: Lib, importc: "sqlite3_free".}
|
||||
#function sqlite3_snprintf(_para1:longint; _para2:Pchar; _para3:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_snprintf';
|
||||
proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl,
|
||||
proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl,
|
||||
dynlib: Lib, varargs, importc: "sqlite3_snprintf".}
|
||||
proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32,
|
||||
proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32,
|
||||
para3: cstring, para4: cstring, para5: cstring, para6: cstring): int32{.
|
||||
cdecl.}, pUserData: pointer): int32{.cdecl, dynlib: Lib,
|
||||
cdecl.}, pUserData: pointer): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_set_authorizer".}
|
||||
proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
|
||||
para3: pointer): pointer{.cdecl, dynlib: Lib,
|
||||
proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
|
||||
para3: pointer): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_trace".}
|
||||
proc progress_handler*(para1: PSqlite3, para2: int32,
|
||||
para3: proc (para1: pointer): int32{.cdecl.},
|
||||
para4: pointer){.cdecl, dynlib: Lib,
|
||||
proc progress_handler*(para1: PSqlite3, para2: int32,
|
||||
para3: proc (para1: pointer): int32{.cdecl.},
|
||||
para4: pointer){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_progress_handler".}
|
||||
proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
|
||||
para3: pointer): pointer{.cdecl, dynlib: Lib,
|
||||
proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
|
||||
para3: pointer): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_commit_hook".}
|
||||
proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
|
||||
proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_open".}
|
||||
proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
|
||||
proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_open16".}
|
||||
proc errcode*(db: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_errcode".}
|
||||
proc errmsg*(para1: PSqlite3): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_errmsg".}
|
||||
proc errmsg16*(para1: PSqlite3): pointer{.cdecl, dynlib: Lib,
|
||||
proc errmsg16*(para1: PSqlite3): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_errmsg16".}
|
||||
proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt,
|
||||
pzTail: ptr cstring): int32{.cdecl, dynlib: Lib,
|
||||
proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt,
|
||||
pzTail: ptr cstring): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_prepare".}
|
||||
|
||||
|
||||
proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var Pstmt,
|
||||
pzTail: ptr cstring): cint {.
|
||||
importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.}
|
||||
|
||||
proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt,
|
||||
pzTail: var pointer): int32{.cdecl, dynlib: Lib,
|
||||
|
||||
proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt,
|
||||
pzTail: var pointer): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_prepare16".}
|
||||
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
|
||||
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
|
||||
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_blob".}
|
||||
proc bind_double*(para1: Pstmt, para2: int32, para3: float64): int32{.cdecl,
|
||||
proc bind_double*(para1: Pstmt, para2: int32, para3: float64): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_bind_double".}
|
||||
proc bind_int*(para1: Pstmt, para2: int32, para3: int32): int32{.cdecl,
|
||||
proc bind_int*(para1: Pstmt, para2: int32, para3: int32): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_bind_int".}
|
||||
proc bind_int64*(para1: Pstmt, para2: int32, para3: int64): int32{.cdecl,
|
||||
proc bind_int64*(para1: Pstmt, para2: int32, para3: int64): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_bind_int64".}
|
||||
proc bind_null*(para1: Pstmt, para2: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_null*(para1: Pstmt, para2: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_null".}
|
||||
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
|
||||
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
|
||||
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_text".}
|
||||
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
|
||||
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
|
||||
para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_text16".}
|
||||
#function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value';
|
||||
#These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT
|
||||
#It's the c world man ;-)
|
||||
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
|
||||
para5: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
|
||||
para5: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_blob".}
|
||||
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
|
||||
para5: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
|
||||
para5: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_text".}
|
||||
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
|
||||
para5: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
|
||||
para5: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_text16".}
|
||||
proc bind_parameter_count*(para1: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
proc bind_parameter_count*(para1: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_bind_parameter_count".}
|
||||
proc bind_parameter_name*(para1: Pstmt, para2: int32): cstring{.cdecl,
|
||||
proc bind_parameter_name*(para1: Pstmt, para2: int32): cstring{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_bind_parameter_name".}
|
||||
proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl,
|
||||
proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_bind_parameter_index".}
|
||||
#function sqlite3_clear_bindings(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_clear_bindings';
|
||||
proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_count".}
|
||||
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
|
||||
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_name".}
|
||||
proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
|
||||
proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_name16".}
|
||||
proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,
|
||||
proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_decltype".}
|
||||
proc column_decltype16*(para1: Pstmt, para2: int32): pointer{.cdecl,
|
||||
proc column_decltype16*(para1: Pstmt, para2: int32): pointer{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_column_decltype16".}
|
||||
proc step*(para1: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_step".}
|
||||
proc data_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
proc data_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_data_count".}
|
||||
proc column_blob*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
|
||||
proc column_blob*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_blob".}
|
||||
proc column_bytes*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc column_bytes*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_bytes".}
|
||||
proc column_bytes16*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc column_bytes16*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_bytes16".}
|
||||
proc column_double*(para1: Pstmt, iCol: int32): float64{.cdecl, dynlib: Lib,
|
||||
proc column_double*(para1: Pstmt, iCol: int32): float64{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_double".}
|
||||
proc column_int*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc column_int*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_int".}
|
||||
proc column_int64*(para1: Pstmt, iCol: int32): int64{.cdecl, dynlib: Lib,
|
||||
proc column_int64*(para1: Pstmt, iCol: int32): int64{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_int64".}
|
||||
proc column_text*(para1: Pstmt, iCol: int32): cstring{.cdecl, dynlib: Lib,
|
||||
proc column_text*(para1: Pstmt, iCol: int32): cstring{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_text".}
|
||||
proc column_text16*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
|
||||
proc column_text16*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_text16".}
|
||||
proc column_type*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
proc column_type*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_type".}
|
||||
proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_finalize".}
|
||||
proc reset*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_reset".}
|
||||
proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
|
||||
eTextRep: int32, para5: pointer,
|
||||
xFunc: Create_function_func_func,
|
||||
xStep: Create_function_step_func,
|
||||
xFinal: Create_function_final_func): int32{.cdecl,
|
||||
proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
|
||||
eTextRep: int32, para5: pointer,
|
||||
xFunc: Create_function_func_func,
|
||||
xStep: Create_function_step_func,
|
||||
xFinal: Create_function_final_func): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_create_function".}
|
||||
proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
|
||||
eTextRep: int32, para5: pointer,
|
||||
xFunc: Create_function_func_func,
|
||||
xStep: Create_function_step_func,
|
||||
xFinal: Create_function_final_func): int32{.cdecl,
|
||||
proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
|
||||
eTextRep: int32, para5: pointer,
|
||||
xFunc: Create_function_func_func,
|
||||
xStep: Create_function_step_func,
|
||||
xFinal: Create_function_final_func): int32{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_create_function16".}
|
||||
proc aggregate_count*(para1: Pcontext): int32{.cdecl, dynlib: Lib,
|
||||
proc aggregate_count*(para1: Pcontext): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_aggregate_count".}
|
||||
proc value_blob*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
proc value_blob*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_blob".}
|
||||
proc value_bytes*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
proc value_bytes*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_bytes".}
|
||||
proc value_bytes16*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
proc value_bytes16*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_bytes16".}
|
||||
proc value_double*(para1: Pvalue): float64{.cdecl, dynlib: Lib,
|
||||
proc value_double*(para1: Pvalue): float64{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_double".}
|
||||
proc value_int*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
proc value_int*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_int".}
|
||||
proc value_int64*(para1: Pvalue): int64{.cdecl, dynlib: Lib,
|
||||
proc value_int64*(para1: Pvalue): int64{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_int64".}
|
||||
proc value_text*(para1: Pvalue): cstring{.cdecl, dynlib: Lib,
|
||||
proc value_text*(para1: Pvalue): cstring{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_text".}
|
||||
proc value_text16*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
proc value_text16*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_text16".}
|
||||
proc value_text16le*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
proc value_text16le*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_text16le".}
|
||||
proc value_text16be*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
proc value_text16be*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_text16be".}
|
||||
proc value_type*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
proc value_type*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_value_type".}
|
||||
proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl,
|
||||
proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_aggregate_context".}
|
||||
proc user_data*(para1: Pcontext): pointer{.cdecl, dynlib: Lib,
|
||||
proc user_data*(para1: Pcontext): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_user_data".}
|
||||
proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, dynlib: Lib,
|
||||
proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_get_auxdata".}
|
||||
proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
|
||||
para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib,
|
||||
proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
|
||||
para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_set_auxdata".}
|
||||
proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_blob".}
|
||||
proc result_double*(para1: Pcontext, para2: float64){.cdecl, dynlib: Lib,
|
||||
proc result_double*(para1: Pcontext, para2: float64){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_double".}
|
||||
proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl,
|
||||
proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_result_error".}
|
||||
proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl,
|
||||
proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl,
|
||||
dynlib: Lib, importc: "sqlite3_result_error16".}
|
||||
proc result_int*(para1: Pcontext, para2: int32){.cdecl, dynlib: Lib,
|
||||
proc result_int*(para1: Pcontext, para2: int32){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_int".}
|
||||
proc result_int64*(para1: Pcontext, para2: int64){.cdecl, dynlib: Lib,
|
||||
proc result_int64*(para1: Pcontext, para2: int64){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_int64".}
|
||||
proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib,
|
||||
proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_null".}
|
||||
proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_text".}
|
||||
proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_text16".}
|
||||
proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_text16le".}
|
||||
proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
|
||||
para4: Result_func){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_text16be".}
|
||||
proc result_value*(para1: Pcontext, para2: Pvalue){.cdecl, dynlib: Lib,
|
||||
proc result_value*(para1: Pcontext, para2: Pvalue){.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_result_value".}
|
||||
proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32,
|
||||
proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32,
|
||||
para4: pointer, xCompare: Create_collation_func): int32{.
|
||||
cdecl, dynlib: Lib, importc: "sqlite3_create_collation".}
|
||||
proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32,
|
||||
proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32,
|
||||
para4: pointer, xCompare: Create_collation_func): int32{.
|
||||
cdecl, dynlib: Lib, importc: "sqlite3_create_collation16".}
|
||||
proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
|
||||
@@ -350,10 +350,10 @@ proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_
|
||||
proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
|
||||
cdecl, dynlib: Lib, importc: "sqlite3_collation_needed16".}
|
||||
proc libversion*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
|
||||
#Alias for allowing better code portability (win32 is not working with external variables)
|
||||
#Alias for allowing better code portability (win32 is not working with external variables)
|
||||
proc version*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
|
||||
# Not published functions
|
||||
proc libversion_number*(): int32{.cdecl, dynlib: Lib,
|
||||
proc libversion_number*(): int32{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_libversion_number".}
|
||||
#function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key';
|
||||
#function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey';
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
type
|
||||
CcState {.pure, final.} = object
|
||||
PccState* = ptr CcState
|
||||
|
||||
|
||||
ErrorFunc* = proc (opaque: pointer, msg: cstring) {.cdecl.}
|
||||
{.deprecated: [TccState: CcState, TErrorFunc: ErrorFunc].}
|
||||
|
||||
@@ -31,33 +31,33 @@ proc setWarning*(s: PccState, warningName: cstring, value: int) {.cdecl,
|
||||
importc: "tcc_set_warning".}
|
||||
## set/reset a warning
|
||||
|
||||
# preprocessor
|
||||
# preprocessor
|
||||
|
||||
proc addIncludePath*(s: PccState, pathname: cstring) {.cdecl,
|
||||
proc addIncludePath*(s: PccState, pathname: cstring) {.cdecl,
|
||||
importc: "tcc_add_include_path".}
|
||||
## add include path
|
||||
|
||||
proc addSysincludePath*(s: PccState, pathname: cstring) {.cdecl,
|
||||
proc addSysincludePath*(s: PccState, pathname: cstring) {.cdecl,
|
||||
importc: "tcc_add_sysinclude_path".}
|
||||
## add in system include path
|
||||
|
||||
|
||||
proc defineSymbol*(s: PccState, sym, value: cstring) {.cdecl,
|
||||
proc defineSymbol*(s: PccState, sym, value: cstring) {.cdecl,
|
||||
importc: "tcc_define_symbol".}
|
||||
## define preprocessor symbol 'sym'. Can put optional value
|
||||
|
||||
proc undefineSymbol*(s: PccState, sym: cstring) {.cdecl,
|
||||
proc undefineSymbol*(s: PccState, sym: cstring) {.cdecl,
|
||||
importc: "tcc_undefine_symbol".}
|
||||
## undefine preprocess symbol 'sym'
|
||||
|
||||
# compiling
|
||||
# compiling
|
||||
|
||||
proc addFile*(s: PccState, filename: cstring): cint {.cdecl,
|
||||
proc addFile*(s: PccState, filename: cstring): cint {.cdecl,
|
||||
importc: "tcc_add_file".}
|
||||
## add a file (either a C file, dll, an object, a library or an ld
|
||||
## script). Return -1 if error.
|
||||
|
||||
proc compileString*(s: PccState, buf: cstring): cint {.cdecl,
|
||||
proc compileString*(s: PccState, buf: cstring): cint {.cdecl,
|
||||
importc: "tcc_compile_string".}
|
||||
## compile a string containing a C source. Return non zero if error.
|
||||
|
||||
@@ -71,12 +71,12 @@ const
|
||||
OutputDll*: cint = 2 ## dynamic library
|
||||
OutputObj*: cint = 3 ## object file
|
||||
OutputPreprocess*: cint = 4 ## preprocessed file (used internally)
|
||||
|
||||
|
||||
OutputFormatElf*: cint = 0 ## default output format: ELF
|
||||
OutputFormatBinary*: cint = 1 ## binary image output
|
||||
OutputFormatCoff*: cint = 2 ## COFF
|
||||
|
||||
proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
|
||||
proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
|
||||
importc: "tcc_set_output_type".}
|
||||
## set output type. MUST BE CALLED before any compilation
|
||||
|
||||
@@ -115,5 +115,5 @@ proc getSymbol*(s: PccState, name: cstring): pointer {.cdecl,
|
||||
proc setLibPath*(s: PccState, path: cstring) {.cdecl,
|
||||
importc: "tcc_set_lib_path".}
|
||||
## set CONFIG_TCCDIR at runtime
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user