diff options
author | Araq <rumpf_a@web.de> | 2016-03-29 15:30:44 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2016-03-29 15:31:10 +0200 |
commit | db2b2156581ffaaa63e7ff9c0a07ced540006956 (patch) | |
tree | 341535aa355ef422ee886f7b14fda6fbfeae0523 /lib | |
parent | 8bf14b12a963980e71ad46e16a8a2e35c9465b8c (diff) | |
parent | b4f1eef3a3b0ff56d0a68c15c43ffecb7778e939 (diff) | |
download | Nim-db2b2156581ffaaa63e7ff9c0a07ced540006956.tar.gz |
resolved merge conflicts
Diffstat (limited to 'lib')
-rw-r--r-- | lib/core/locks.nim | 10 | ||||
-rw-r--r-- | lib/deprecated/pure/sockets.nim | 16 | ||||
-rw-r--r-- | lib/impure/db_odbc.nim | 202 | ||||
-rw-r--r-- | lib/nimbase.h | 2 | ||||
-rw-r--r-- | lib/posix/kqueue.nim | 134 | ||||
-rw-r--r-- | lib/posix/posix.nim | 6 | ||||
-rw-r--r-- | lib/pure/collections/sequtils.nim | 47 | ||||
-rw-r--r-- | lib/pure/collections/sharedlist.nim | 95 | ||||
-rw-r--r-- | lib/pure/collections/tableimpl.nim | 5 | ||||
-rw-r--r-- | lib/pure/logging.nim | 13 | ||||
-rw-r--r-- | lib/pure/math.nim | 1 | ||||
-rw-r--r-- | lib/pure/nativesockets.nim | 66 | ||||
-rw-r--r-- | lib/pure/net.nim | 2 | ||||
-rw-r--r-- | lib/pure/os.nim | 25 | ||||
-rw-r--r-- | lib/pure/rationals.nim | 2 | ||||
-rw-r--r-- | lib/pure/ropes.nim | 13 | ||||
-rw-r--r-- | lib/pure/strutils.nim | 26 | ||||
-rw-r--r-- | lib/system.nim | 4 | ||||
-rw-r--r-- | lib/system/atomics.nim | 47 | ||||
-rw-r--r-- | lib/system/dyncalls.nim | 14 | ||||
-rw-r--r-- | lib/system/gc_stack.nim | 382 | ||||
-rw-r--r-- | lib/system/jssys.nim | 4 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 2 | ||||
-rw-r--r-- | lib/system/osalloc.nim | 4 | ||||
-rw-r--r-- | lib/system/repr.nim | 17 | ||||
-rw-r--r-- | lib/system/sysio.nim | 26 | ||||
-rw-r--r-- | lib/windows/winlean.nim | 16 |
27 files changed, 974 insertions, 207 deletions
diff --git a/lib/core/locks.nim b/lib/core/locks.nim index f1a74876e..66e0ab520 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -54,3 +54,13 @@ proc wait*(cond: var Cond, lock: var Lock) {.inline.} = proc signal*(cond: var Cond) {.inline.} = ## sends a signal to the condition variable `cond`. signalSysCond(cond) + +template withLock*(a: Lock, body: untyped) = + ## Acquires the given lock, executes the statements in body and + ## releases the lock after the statements finish executing. + a.acquire() + {.locks: [a].}: + try: + body + finally: + a.release() \ No newline at end of file diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index f7d0950d8..20e6d9364 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -206,6 +206,18 @@ proc htons*(x: int16): int16 = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. result = sockets.ntohs(x) +template ntohl(x: uint32): expr = + cast[uint32](sockets.ntohl(cast[int32](x))) + +template ntohs(x: uint16): expr = + cast[uint16](sockets.ntohs(cast[int16](x))) + +template htonl(x: uint32): expr = + sockets.ntohl(x) + +template htons(x: uint16): expr = + sockets.ntohs(x) + when defined(Posix): proc toInt(domain: Domain): cint = case domain @@ -451,7 +463,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. name.sin_family = int16(ord(AF_INET)) else: name.sin_family = posix.AF_INET - name.sin_port = sockets.htons(int16(port)) + name.sin_port = sockets.htons(uint16(port)) name.sin_addr.s_addr = sockets.htonl(INADDR_ANY) if bindSocket(socket.fd, cast[ptr SockAddr](addr(name)), sizeof(name).SockLen) < 0'i32: @@ -834,7 +846,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), when false: var s: TSockAddrIn s.sin_addr.s_addr = inet_addr(address) - s.sin_port = sockets.htons(int16(port)) + s.sin_port = sockets.htons(uint16(port)) when defined(windows): s.sin_family = toU16(ord(af)) else: diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 4f0b0469d..3a14e6304 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -38,7 +38,7 @@ ## ## .. code-block:: Nim ## import db_odbc -## let db = open("localhost", "user", "password", "dbname") +## var db = open("localhost", "user", "password", "dbname") ## db.close() ## ## Creating a table @@ -64,7 +64,7 @@ ## ## import db_odbc, math ## -## let theDb = open("localhost", "nim", "nim", "test") +## var theDb = open("localhost", "nim", "nim", "test") ## ## theDb.exec(sql"Drop table if exists myTestTbl") ## theDb.exec(sql("create table myTestTbl (" & @@ -88,9 +88,7 @@ ## ## theDb.close() - import strutils, odbcsql - import db_common export db_common @@ -169,11 +167,11 @@ proc dbError*(db: var DbConn) {. properFreeResult(SQL_HANDLE_ENV, db.env) raise e -proc SqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = - ## Wrapper that checks if ``resVal`` is not SQL_SUCCESS and if so, raises [EDb] - if resVal != SQL_SUCCESS: dbError(db) +proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = + ## Wrapper that raises [EDb] if ``resVal`` is neither SQL_SUCCESS or SQL_NO_DATA + if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db) -proc SqlGetDBMS(db: var DbConn): string {. +proc sqlGetDBMS(db: var DbConn): string {. tags: [ReadDbEffect, WriteDbEffect], raises: [] .} = ## Returns the ODBC SQL_DBMS_NAME string const @@ -182,7 +180,7 @@ proc SqlGetDBMS(db: var DbConn): string {. sz: TSqlSmallInt = 0 buf[0] = '\0' try: - db.SqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), + db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr)) except: discard return $buf.cstring @@ -212,7 +210,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {. add(result, c) proc prepareFetch(db: var DbConn, query: SqlQuery, - args: varargs[string, `$`]) {. + args: varargs[string, `$`]) : TSqlSmallInt {. tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = # Prepare a statement, execute it and fetch the data to the driver # ready for retrieval of the data @@ -220,11 +218,13 @@ proc prepareFetch(db: var DbConn, query: SqlQuery, # requires calling # properFreeResult(SQL_HANDLE_STMT, db.stmt) # when finished - db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) var q = dbFormat(query, args) - db.SqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) - db.SqlCheck(SQLExecute(db.stmt)) - db.SqlCheck(SQLFetch(db.stmt)) + db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + db.sqlCheck(SQLExecute(db.stmt)) + var retcode = SQLFetch(db.stmt) + db.sqlCheck(retcode) + result=retcode proc prepareFetchDirect(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {. @@ -235,10 +235,10 @@ proc prepareFetchDirect(db: var DbConn, query: SqlQuery, # requires calling # properFreeResult(SQL_HANDLE_STMT, db.stmt) # when finished - db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) var q = dbFormat(query, args) - db.SqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) - db.SqlCheck(SQLFetch(db.stmt)) + db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + db.sqlCheck(SQLFetch(db.stmt)) proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. tags: [ReadDbEffect, WriteDbEffect], raises: [].} = @@ -285,20 +285,30 @@ iterator fastRows*(db: var DbConn, query: SqlQuery, rowRes: Row sz: TSqlSmallInt = 0 cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - rowRes = newRow(cCnt) - for rNr in 1..rCnt: - for colId in 1..cCnt: - buf[0] = '\0' - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) - rowRes[colId-1] = $buf.cstring - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) - yield rowRes + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + # tempcCnt,A field to store the number of temporary variables, for unknown reasons, + # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + # so the values of the temporary variable to store the cCnt. + # After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + discard + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + yield rowRes + res = SQLFetch(db.stmt) + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) iterator instantRows*(db: var DbConn, query: SqlQuery, @@ -310,19 +320,30 @@ iterator instantRows*(db: var DbConn, query: SqlQuery, rowRes: Row sz: TSqlSmallInt = 0 cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - rowRes = newRow(cCnt) - for rNr in 1..rCnt: - for colId in 1..cCnt: - buf[0] = '\0' - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) - rowRes[colId-1] = $buf.cstring - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) - yield (row: rowRes, len: cCnt.int) + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + # tempcCnt,A field to store the number of temporary variables, for unknown reasons, + # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + # so the values of the temporary variable to store the cCnt. + # After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + discard + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + yield (row: rowRes, len: cCnt.int) + res = SQLFetch(db.stmt) + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) proc `[]`*(row: InstantRow, col: int): string {.inline.} = @@ -339,43 +360,68 @@ proc getRow*(db: var DbConn, query: SqlQuery, ## Retrieves a single row. If the query doesn't return any rows, this proc ## will return a Row with empty strings for each column. var + rowRes: Row sz: TSqlSmallInt = 0.TSqlSmallInt cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - result = @[] - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - for colId in 1..cCnt: - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) - result.add($buf.cstring) - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + ## tempcCnt,A field to store the number of temporary variables, for unknown reasons, + ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + ## so the values of the temporary variable to store the cCnt. + ## After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + result = @[] + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + res = SQLFetch(db.stmt) + result = rowRes + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) proc getAllRows*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): seq[Row] {. - tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} = ## Executes the query and returns the whole result dataset. var + rows: seq[Row] = @[] rowRes: Row sz: TSqlSmallInt = 0 cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - result = @[] - for rNr in 1..rCnt: - rowRes = @[] - buf[0] = '\0' - for colId in 1..cCnt: - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr)) - rowRes.add($buf.cstring) - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) - result.add(rowRes) + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + ## tempcCnt,A field to store the number of temporary variables, for unknown reasons, + ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + ## so the values of the temporary variable to store the cCnt. + ## After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + result = @[] + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + rows.add(rowRes) + res = SQLFetch(db.stmt) + result = rows + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) iterator rows*(db: var DbConn, query: SqlQuery, @@ -407,10 +453,9 @@ proc tryInsertId*(db: var DbConn, query: SqlQuery, if not tryExec(db, query, args): result = -1'i64 else: - echo "DBMS: ",SqlGetDBMS(db).toLower() result = -1'i64 try: - case SqlGetDBMS(db).toLower(): + case sqlGetDBMS(db).toLower(): of "postgresql": result = getValue(db, sql"SELECT LASTVAL();", []).parseInt of "mysql": @@ -438,15 +483,12 @@ proc execAffectedRows*(db: var DbConn, query: SqlQuery, ## Runs the query (typically "UPDATE") and returns the ## number of affected rows result = -1 - var res = SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle) - if res != SQL_SUCCESS: dbError(db) + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle)) var q = dbFormat(query, args) - res = SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt) - if res != SQL_SUCCESS: dbError(db) + db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) rawExec(db, query, args) var rCnt = -1 - result = SQLRowCount(db.hDb, rCnt) - if res != SQL_SUCCESS: dbError(db) + db.sqlCheck(SQLRowCount(db.hDb, rCnt)) properFreeResult(SQL_HANDLE_STMT, db.stmt) result = rCnt @@ -501,5 +543,5 @@ proc setEncoding*(connection: DbConn, encoding: string): bool {. ## ## Sets the encoding of a database connection, returns true for ## success, false for failure. - #result = set_character_set(connection, encoding) == 0 - dbError("setEncoding() is currently not implemented by the db_odbc module") + ##result = set_character_set(connection, encoding) == 0 + dbError("setEncoding() is currently not implemented by the db_odbc module") \ No newline at end of file diff --git a/lib/nimbase.h b/lib/nimbase.h index 266b84b09..5a4f403b6 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -108,6 +108,8 @@ __clang__ defined __SUNPRO_C || \ defined __xlC__ # define NIM_THREADVAR __thread +#elif defined __TINYC__ +# define NIM_THREADVAR #else # error "Cannot define NIM_THREADVAR" #endif diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index 511ada9ac..5c67d621e 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2015 Adam Strzelecki +# (c) Copyright 2016 Eugene Kabanov # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -11,20 +11,35 @@ from posix import Timespec -# Filters: -const - EVFILT_READ* = -1 - EVFILT_WRITE* = -2 - EVFILT_AIO* = -3 - EVFILT_VNODE* = -4 - EVFILT_PROC* = -5 - EVFILT_SIGNAL* = -6 - EVFILT_TIMER* = -7 - EVFILT_MACHPORT* = -8 - EVFILT_FS* = -9 - EVFILT_USER* = -10 - # -11 is unused - EVFILT_VM* = -12 +when defined(macosx) or defined(freebsd) or defined(openbsd): + const + EVFILT_READ* = -1 + EVFILT_WRITE* = -2 + EVFILT_AIO* = -3 ## attached to aio requests + EVFILT_VNODE* = -4 ## attached to vnodes + EVFILT_PROC* = -5 ## attached to struct proc + EVFILT_SIGNAL* = -6 ## attached to struct proc + EVFILT_TIMER* = -7 ## timers +elif defined(netbsd): + const + EVFILT_READ* = 0 + EVFILT_WRITE* = 1 + EVFILT_AIO* = 2 ## attached to aio requests + EVFILT_VNODE* = 3 ## attached to vnodes + EVFILT_PROC* = 4 ## attached to struct proc + EVFILT_SIGNAL* = 5 ## attached to struct proc + EVFILT_TIMER* = 6 ## timers (in ms) +when defined(macosx): + const + EVFILT_MACHPORT* = -8 ## Mach portsets + EVFILT_FS* = -9 ## filesystem events + EVFILT_USER* = -10 ## user events + EVFILT_VM = -12 ## virtual memory events +elif defined(freebsd): + const + EVFILT_FS* = -9 ## filesystem events + EVFILT_LIO* = -10 ## attached to lio requests + EVFILT_USER* = -11 ## user events # Actions: const @@ -40,21 +55,92 @@ const EV_CLEAR* = 0x0020 ## Clear event state after reporting. EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0 EV_DISPATCH* = 0x0080 ## Disable event after reporting. + + EV_SYSFLAGS* = 0xF000 ## Reserved by system + EV_DROP* = 0x1000 ## Not should be dropped + EV_FLAG1* = 0x2000 ## Filter-specific flag # Return values: const EV_EOF* = 0x8000 ## EOF detected EV_ERROR* = 0x4000 ## Error, data contains errno + +when defined(macosx) or defined(freebsd): + # EVFILT_USER is not supported by OpenBSD and NetBSD + # + # data/hint flags/masks for EVFILT_USER, shared with userspace + # + # On input, the top two bits of fflags specifies how the lower twenty four + # bits should be applied to the stored value of fflags. + # + # On output, the top two bits will always be set to NOTE_FFNOP and the + # remaining twenty four bits will contain the stored fflags value. + const + NOTE_FFNOP* = 0x00000000'u32 ## ignore input fflags + NOTE_FFAND* = 0x40000000'u32 ## AND fflags + NOTE_FFOR* = 0x80000000'u32 ## OR fflags + NOTE_FFCOPY* = 0xc0000000'u32 ## copy fflags + NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations + NOTE_FFLAGSMASK* = 0x00ffffff'u32 + + NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered + ## for output. + +# data/hint flags for EVFILT_{READ|WRITE}, shared with userspace +const + NOTE_LOWAT* = 0x0001 ## low water mark + +# data/hint flags for EVFILT_VNODE, shared with userspace +const + NOTE_DELETE* = 0x0001 ## vnode was removed + NOTE_WRITE* = 0x0002 ## data contents changed + NOTE_EXTEND* = 0x0004 ## size increased + NOTE_ATTRIB* = 0x0008 ## attributes changed + NOTE_LINK* = 0x0010 ## link count changed + NOTE_RENAME* = 0x0020 ## vnode was renamed + NOTE_REVOKE* = 0x0040 ## vnode access was revoked + +# data/hint flags for EVFILT_PROC, shared with userspace +const + NOTE_EXIT* = 0x80000000'u32 ## process exited + NOTE_FORK* = 0x40000000'u32 ## process forked + NOTE_EXEC* = 0x20000000'u32 ## process exec'd + NOTE_PCTRLMASK* = 0xf0000000'u32 ## mask for hint bits + NOTE_PDATAMASK* = 0x000fffff'u32 ## mask for pid + +# additional flags for EVFILT_PROC +const + NOTE_TRACK* = 0x00000001'u32 ## follow across forks + NOTE_TRACKERR* = 0x00000002'u32 ## could not track child + NOTE_CHILD* = 0x00000004'u32 ## am a child process + +when defined(macosx) or defined(freebsd): + # additional flags for EVFILE_TIMER + const + NOTE_SECONDS* = 0x00000001'u32 ## data is seconds + NOTE_MSECONDS* = 0x00000002'u32 ## data is milliseconds + NOTE_USECONDS* = 0x00000004'u32 ## data is microseconds + NOTE_NSECONDS* = 0x00000008'u32 ## data is nanoseconds +else: + # NetBSD and OpenBSD doesnt support NOTE_{TIME} constants, but + # support EVFILT_TIMER with granularity of milliseconds. + const + NOTE_MSECONDS* = 0x00000000'u32 + type + ## This define not fully satisfy NetBSD "struct kevent" + ## but it works and tested. KEvent* {.importc: "struct kevent", - header: "<sys/event.h>", pure, final.} = object - ident*: cuint ## identifier for this event (uintptr_t) - filter*: cshort ## filter for event - flags*: cushort ## general flags - fflags*: cuint ## filter-specific flags - data*: cuint ## filter-specific data (intptr_t) - #udata*: ptr void ## opaque user data identifier + header: """#include <sys/types.h> + #include <sys/event.h> + #include <sys/time.h>""", pure, final.} = object + ident* : uint ## identifier for this event (uintptr_t) + filter* : cshort ## filter for event + flags* : cushort ## general flags + fflags* : cuint ## filter-specific flags + data* : int ## filter-specific data (intptr_t) + udata* : pointer ## opaque user data identifier proc kqueue*(): cint {.importc: "kqueue", header: "<sys/event.h>".} ## Creates new queue and returns its descriptor. @@ -65,7 +151,7 @@ proc kevent*(kqFD: cint, {.importc: "kevent", header: "<sys/event.h>".} ## Manipulates queue for given ``kqFD`` descriptor. -proc EV_SET*(event: ptr KEvent, ident: cuint, filter: cshort, flags: cushort, - fflags: cuint, data: cuint, udata: ptr void) +proc EV_SET*(event: ptr KEvent, ident: uint, filter: cshort, flags: cushort, + fflags: cuint, data: int, udata: pointer) {.importc: "EV_SET", header: "<sys/event.h>".} ## Fills event with given data. diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 40b48f992..4c7b817cb 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -486,11 +486,11 @@ type l_onoff*: cint ## Indicates whether linger option is enabled. l_linger*: cint ## Linger time, in seconds. - InPort* = int16 ## unsigned! - InAddrScalar* = int32 ## unsigned! + InPort* = uint16 + InAddrScalar* = uint32 InAddrT* {.importc: "in_addr_t", pure, final, - header: "<netinet/in.h>".} = int32 ## unsigned! + header: "<netinet/in.h>".} = uint32 InAddr* {.importc: "struct in_addr", pure, final, header: "<netinet/in.h>".} = object ## struct in_addr diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index b72face91..0e3824a81 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -508,13 +508,39 @@ template foldl*(sequence, operation: expr): expr = ## assert subtraction == -15, "Subtraction is (((5)-9)-11)" ## assert multiplication == 495, "Multiplication is (((5)*9)*11)" ## assert concatenation == "nimiscool" - assert sequence.len > 0, "Can't fold empty sequences" - var result {.gensym.}: type(sequence[0]) - result = sequence[0] - for i in 1..<sequence.len: + let s = sequence + assert s.len > 0, "Can't fold empty sequences" + var result {.gensym.}: type(s[0]) + result = s[0] + for i in 1..<s.len: let a {.inject.} = result - b {.inject.} = sequence[i] + b {.inject.} = s[i] + result = operation + result + +template foldl*(sequence, operation: expr, first): expr = + ## Template to fold a sequence from left to right, returning the accumulation. + ## + ## This version of ``foldl`` gets a starting parameter. This makes it possible + ## to accumulate the sequence into a different type than the sequence elements. + ## + ## The ``operation`` parameter should be an expression which uses the variables + ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the + ## start value (the first ``a``) and therefor defines the type of the result. + ## Example: + ## + ## .. code-block:: + ## let + ## numbers = @[0, 8, 1, 5] + ## digits = foldl(numbers, a & (chr(b + ord('0'))), "") + ## assert digits == "0815" + var result {.gensym.}: type(first) + result = first + for x in items(sequence): + let + a {.inject.} = result + b {.inject.} = x result = operation result @@ -544,12 +570,13 @@ template foldr*(sequence, operation: expr): expr = ## assert subtraction == 7, "Subtraction is (5-(9-(11)))" ## assert multiplication == 495, "Multiplication is (5*(9*(11)))" ## assert concatenation == "nimiscool" - assert sequence.len > 0, "Can't fold empty sequences" - var result {.gensym.}: type(sequence[0]) - result = sequence[sequence.len - 1] - for i in countdown(sequence.len - 2, 0): + let s = sequence + assert s.len > 0, "Can't fold empty sequences" + var result {.gensym.}: type(s[0]) + result = sequence[s.len - 1] + for i in countdown(s.len - 2, 0): let - a {.inject.} = sequence[i] + a {.inject.} = s[i] b {.inject.} = result result = operation result diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim new file mode 100644 index 000000000..e93ceb02f --- /dev/null +++ b/lib/pure/collections/sharedlist.nim @@ -0,0 +1,95 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Shared list support. + +{.push stackTrace:off.} + +import + locks + +const + ElemsPerNode = 100 + +type + SharedListNode[A] = ptr object + next: SharedListNode[A] + dataLen: int + d: array[ElemsPerNode, A] + + SharedList*[A] = object ## generic shared list + head, tail: SharedListNode[A] + lock*: Lock + +template withLock(t, x: untyped) = + acquire(t.lock) + x + release(t.lock) + +proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = + ## iterates over the list. If 'action' returns true, the + ## current item is removed from the list. + withLock(x): + var n = x.head + while n != nil: + var i = 0 + while i < n.dataLen: + # action can add new items at the end, so release the lock: + release(x.lock) + if action(n.d[i]): + acquire(x.lock) + let t = x.tail + n.d[i] = t.d[t.dataLen] + dec t.dataLen + else: + acquire(x.lock) + inc i + n = n.next + +iterator items*[A](x: var SharedList[A]): A = + withLock(x): + var it = x.head + while it != nil: + for i in 0..it.dataLen-1: + yield it.d[i] + it = it.next + +proc add*[A](x: var SharedList[A]; y: A) = + withLock(x): + var node: SharedListNode[A] + if x.tail == nil or x.tail.dataLen == ElemsPerNode: + node = cast[type node](allocShared0(sizeof(node[]))) + node.next = x.tail + x.tail = node + if x.head == nil: x.head = node + else: + node = x.tail + node.d[node.dataLen] = y + inc(node.dataLen) + +proc initSharedList*[A](): SharedList[A] = + initLock result.lock + result.head = nil + result.tail = nil + +proc clear*[A](t: var SharedList[A]) = + withLock(t): + var it = t.head + while it != nil: + let nxt = it.next + deallocShared(it) + it = nxt + t.head = nil + t.tail = nil + +proc deinitSharedList*[A](t: var SharedList[A]) = + clear(t) + deinitLock t.lock + +{.pop.} diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index beafe1109..e4ec05b1c 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -129,4 +129,7 @@ template delImpl() {.dirty, immediate.} = r = t.data[i].hcode and msk # "home" location of key@i if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): break - shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop + when defined(js): + t.data[j] = t.data[i] + else: + shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index aa55b5ade..f602ce31d 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -92,6 +92,10 @@ type {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} +var + level {.threadvar.}: Level ## global log filter + handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels + proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string = ## Format a log message using the ``frmt`` format string, ``level`` and varargs. ## See the module documentation for the format string syntax. @@ -133,13 +137,13 @@ method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {. method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## Logs to the console using ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: writeLine(stdout, substituteLog(logger.fmtStr, level, args)) if level in {lvlError, lvlFatal}: flushFile(stdout) method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) if level in {lvlError, lvlFatal}: flushFile(logger.file) @@ -224,7 +228,7 @@ proc rotate(logger: RollingFileLogger) = method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using rolling ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: if logger.curLine >= logger.maxLines: logger.file.close() rotate(logger) @@ -238,9 +242,6 @@ method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) # -------- -var level {.threadvar.}: Level ## global log filter -var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels - proc logLoop(level: Level, args: varargs[string, `$`]) = for logger in items(handlers): if level >= logger.levelThreshold: diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b0104336e..84c8d3b11 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -44,6 +44,7 @@ when not defined(js) and not defined(nimscript): const PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) + TAU* = 2.0 * PI ## the circle constant TAU (= 2 * PI) E* = 2.71828182845904523536028747 ## Euler's number MaxFloat64Precision* = 16 ## maximum number of meaningful digits diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 3951b46a3..043d6d80a 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -219,31 +219,67 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, proc dealloc*(ai: ptr AddrInfo) = freeaddrinfo(ai) -proc ntohl*(x: int32): int32 = - ## Converts 32-bit integers from network to host byte order. +proc ntohl*(x: uint32): uint32 = + ## Converts 32-bit unsigned 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. when cpuEndian == bigEndian: result = x - else: result = (x shr 24'i32) or - (x shr 8'i32 and 0xff00'i32) or - (x shl 8'i32 and 0xff0000'i32) or - (x shl 24'i32) + else: result = (x shr 24'u32) or + (x shr 8'u32 and 0xff00'u32) or + (x shl 8'u32 and 0xff0000'u32) or + (x shl 24'u32) -proc ntohs*(x: int16): int16 = - ## Converts 16-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 2-byte swap operation. +template ntohl*(x: int32): expr {.deprecated.} = + ## 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. + ## **Warning**: This template is deprecated since 0.14.0, IPv4 + ## addresses are now treated as unsigned integers. Please use the unsigned + ## version of this template. + cast[int32](ntohl(cast[uint32](x))) + +proc ntohs*(x: uint16): uint16 = + ## Converts 16-bit unsigned 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 2-byte swap operation. when cpuEndian == bigEndian: result = x - else: result = (x shr 8'i16) or (x shl 8'i16) - -template htonl*(x: int32): expr = + else: result = (x shr 8'u16) or (x shl 8'u16) + +template ntohs*(x: int16): expr {.deprecated.} = + ## Converts 16-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 2-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, where port + ## numbers became unsigned integers. Please use the unsigned version of + ## this template. + cast[int16](ntohs(cast[uint16](x))) + +template htonl*(x: int32): expr {.deprecated.} = ## Converts 32-bit integers from host to network byte order. On machines ## where the host byte order is the same as network byte order, this is ## a no-op; otherwise, it performs a 4-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, IPv4 + ## addresses are now treated as unsigned integers. Please use the unsigned + ## version of this template. nativesockets.ntohl(x) -template htons*(x: int16): expr = - ## Converts 16-bit positive integers from host to network byte order. +template htonl*(x: uint32): expr = + ## Converts 32-bit unsigned integers from host to network byte order. On + ## machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + nativesockets.ntohl(x) + +template htons*(x: int16): expr {.deprecated.} = + ## Converts 16-bit integers from host to network byte order. + ## On machines where the host byte order is the same as network byte + ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, where port + ## numbers became unsigned integers. Please use the unsigned version of + ## this template. + nativesockets.ntohs(x) + +template htons*(x: uint16): expr = + ## Converts 16-bit unsigned integers from host to network byte order. ## On machines where the host byte order is the same as network byte ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. nativesockets.ntohs(x) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index b9764edd3..330682ca9 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -368,7 +368,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. name.sin_family = toInt(AF_INET).int16 else: name.sin_family = toInt(AF_INET) - name.sin_port = htons(int16(port)) + name.sin_port = htons(port.uint16) name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)), sizeof(name).SockLen) < 0'i32: diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 017385825..470559e17 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1350,15 +1350,12 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the filename of the application's executable. ## ## This procedure will resolve symlinks. - ## - ## **Note**: This does not work reliably on BSD. # Linux: /proc/<pid>/exe # Solaris: # /proc/<pid>/object/a.out (filename only) # /proc/<pid>/path/a.out (complete pathname) - # *BSD (and maybe Darwin too): - # /proc/<pid>/file + # FreeBSD: /proc/<pid>/file when defined(windows): when useWinUnicode: var buf = newWideCString("", 256) @@ -1368,15 +1365,6 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = result = newString(256) var len = getModuleFileNameA(0, result, 256) setlen(result, int(len)) - elif defined(linux) or defined(aix): - result = getApplAux("/proc/self/exe") - if result.len == 0: result = getApplHeuristic() - elif defined(solaris): - result = getApplAux("/proc/" & $getpid() & "/path/a.out") - if result.len == 0: result = getApplHeuristic() - elif defined(freebsd): - result = getApplAux("/proc/" & $getpid() & "/file") - if result.len == 0: result = getApplHeuristic() elif defined(macosx): var size: cuint32 getExecPath1(nil, size) @@ -1386,9 +1374,15 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = if result.len > 0: result = result.expandFilename else: + when defined(linux) or defined(aix) or defined(netbsd): + result = getApplAux("/proc/self/exe") + elif defined(solaris): + result = getApplAux("/proc/" & $getpid() & "/path/a.out") + elif defined(freebsd): + result = getApplAux("/proc/" & $getpid() & "/file") # little heuristic that may work on other POSIX-like systems: - result = string(getEnv("_")) - if result.len == 0: result = getApplHeuristic() + if result.len == 0: + result = getApplHeuristic() proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} = ## Returns the filename of the application's executable. @@ -1404,7 +1398,6 @@ proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} = proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the directory of the application's executable. - ## **Note**: This does not work reliably on BSD. result = splitFile(getAppFilename()).dir proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 72c64befc..6fd05dc4b 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -164,7 +164,7 @@ proc `-` *[T](x: Rational[T], y: T): Rational[T] = proc `-` *[T](x: T, y: Rational[T]): Rational[T] = ## Subtract rational `y` from int `x`. - result.num = - x * y.den + y.num + result.num = x * y.den - y.num result.den = y.den proc `-=` *[T](x: var Rational[T], y: Rational[T]) = diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index df7071642..6e97237e0 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -134,11 +134,16 @@ proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. if s.len == 0: result = nil - elif cacheEnabled: - result = insertInCache(s, cache) - cache = result else: - result = newRope(s) + when nimvm: + # No caching in VM context + result = newRope(s) + else: + if cacheEnabled: + result = insertInCache(s, cache) + cache = result + else: + result = newRope(s) proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = ## Converts an int to a rope. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a446f85b4..f2c1e77e1 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -882,7 +882,7 @@ proc abbrev*(s: string, possibilities: openArray[string]): int = # --------------------------------------------------------------------------- -proc join*(a: openArray[string], sep: string): string {. +proc join*(a: openArray[string], sep: string = ""): string {. noSideEffect, rtl, extern: "nsuJoinSep".} = ## Concatenates all strings in `a` separating them with `sep`. if len(a) > 0: @@ -896,16 +896,15 @@ proc join*(a: openArray[string], sep: string): string {. else: result = "" -proc join*(a: openArray[string]): string {. - noSideEffect, rtl, extern: "nsuJoin".} = - ## Concatenates all strings in `a`. - if len(a) > 0: - var L = 0 - for i in 0..high(a): inc(L, a[i].len) - result = newStringOfCap(L) - for i in 0..high(a): add(result, a[i]) - else: - result = "" +proc join*[T: not string](a: openArray[T], sep: string = ""): string {. + noSideEffect, rtl.} = + ## Converts all elements in `a` to strings using `$` and concatenates them + ## with `sep`. + result = "" + for i, x in a: + if i > 0: + add(result, sep) + add(result, $x) type SkipTable = array[char, int] @@ -1721,3 +1720,8 @@ when isMainModule: doAssert(not isUpper("AAcc")) doAssert(not isUpper("A#$")) doAssert(unescape(r"\x013", "", "") == "\x013") + + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" diff --git a/lib/system.nim b/lib/system.nim index 241f55224..fefabe53f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1288,6 +1288,10 @@ const hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") +when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): + # tcc doesn't support TLS + {.error: "``--tlsEmulation:on`` must be used when using threads with tcc backend".} + when defined(boehmgc): when defined(windows): const boehmLib = "boehmgc.dll" diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 158fe91bc..a79dcb152 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -197,6 +197,51 @@ when defined(windows) and not someGcc: proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = interlockedCompareExchange(p, cast[int](newValue), cast[int](oldValue)) != 0 # XXX fix for 64 bit build +elif defined(tcc) and not defined(windows): + when defined(amd64): + {.emit:""" +static int __tcc_cas(int *ptr, int oldVal, int newVal) +{ + unsigned char ret; + __asm__ __volatile__ ( + " lock\n" + " cmpxchgq %2,%1\n" + " sete %0\n" + : "=q" (ret), "=m" (*ptr) + : "r" (newVal), "m" (*ptr), "a" (oldVal) + : "memory"); + + if (ret) + return 0; + else + return 1; +} +""".} + else: + assert sizeof(int) == 4 + {.emit:""" +static int __tcc_cas(int *ptr, int oldVal, int newVal) +{ + unsigned char ret; + __asm__ __volatile__ ( + " lock\n" + " cmpxchgl %2,%1\n" + " sete %0\n" + : "=q" (ret), "=m" (*ptr) + : "r" (newVal), "m" (*ptr), "a" (oldVal) + : "memory"); + + if (ret) + return 0; + else + return 1; +} +""".} + + proc tcc_cas(p: ptr int; oldValue, newValue: int): bool + {.importc: "__tcc_cas", nodecl.} + proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = + tcc_cas(cast[ptr int](p), cast[int](oldValue), cast[int](newValue)) else: # this is valid for GCC and Intel C++ proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool @@ -207,7 +252,7 @@ else: when (defined(x86) or defined(amd64)) and someGcc: proc cpuRelax* {.inline.} = {.emit: """asm volatile("pause" ::: "memory");""".} -elif someGcc: +elif someGcc or defined(tcc): proc cpuRelax* {.inline.} = {.emit: """asm volatile("" ::: "memory");""".} elif (defined(x86) or defined(amd64)) and defined(vcc): diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index 6dc8999d1..ba5e4bbeb 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -23,16 +23,16 @@ proc rawWrite(f: File, s: string) = proc nimLoadLibraryError(path: string) = # carefully written to avoid memory allocation: - stdout.rawWrite("could not load: ") - stdout.rawWrite(path) - stdout.rawWrite("\n") + stderr.rawWrite("could not load: ") + stderr.rawWrite(path) + stderr.rawWrite("\n") quit(1) proc procAddrError(name: cstring) {.noinline.} = # carefully written to avoid memory allocation: - stdout.rawWrite("could not import: ") - stdout.write(name) - stdout.rawWrite("\n") + stderr.rawWrite("could not import: ") + stderr.write(name) + stderr.rawWrite("\n") quit(1) # this code was inspired from Lua's source code: @@ -71,7 +71,7 @@ when defined(posix): when defined(nimDebugDlOpen): let error = dlerror() if error != nil: - c_fprintf(c_stdout, "%s\n", error) + c_fprintf(c_stderr, "%s\n", error) proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = result = dlsym(lib, name) diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim new file mode 100644 index 000000000..3a5c5594a --- /dev/null +++ b/lib/system/gc_stack.nim @@ -0,0 +1,382 @@ +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# "Stack GC" for embedded devices or ultra performance requirements. + +include osalloc + +# We manage memory as a thread local stack. Since the allocation pointer +# is detached from the control flow pointer, this model is vastly more +# useful than the traditional programming model while almost as safe. +# Individual objects can also be deleted but no coalescing is performed. +# Stacks can also be moved from one thread to another. + +# We also support 'finalizers'. + +type + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} + # A ref type can have a finalizer that is called before the object's + # storage is freed. + + AlignType = BiggestFloat + ObjHeader = object + typ: PNimType + nextFinal: ptr ObjHeader # next object with finalizer + + Hole = object # stacks can have holes. Otherwise 'growObj' would be insane. + zeroTyp: pointer # overlaid with 'typ' field. Always 'nil'. + size: int # size of the free slot + + Chunk = ptr BaseChunk + BaseChunk = object + next: Chunk + size: int + head, last: ptr ObjHeader # first and last object in chunk that + # has a finalizer attached to it + +type + StackPtr = object + chunk: pointer + remaining: int + current: Chunk + + MemRegion* = object + remaining: int + chunk: pointer + head, last: Chunk + nextChunkSize, totalSize: int + hole: ptr Hole # we support individual freeing + lock: SysLock + +var + region {.threadVar.}: MemRegion + +template withRegion*(r: MemRegion; body: untyped) = + let oldRegion = region + region = r + try: + body + finally: + region = oldRegion + +template inc(p: pointer, s: int) = + p = cast[pointer](cast[int](p) +% s) + +template `+!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + +template `-!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + +proc allocSlowPath(r: var MemRegion; size: int) = + # we need to ensure that the underlying linked list + # stays small. Say we want to grab 16GB of RAM with some + # exponential growth function. So we allocate 16KB, then + # 32 KB, 64 KB, 128KB, 256KB, 512KB, 1MB, 2MB, 4MB, + # 8MB, 16MB, 32MB, 64MB, 128MB, 512MB, 1GB, 2GB, 4GB, 8GB, + # 16GB --> list contains only 20 elements! That's reasonable. + if (r.totalSize and 1) == 0: + r.nextChunkSize = + if r.totalSize < 64 * 1024: PageSize*4 + else: r.nextChunkSize*2 + var s = align(size+sizeof(BaseChunk), PageSize) + var fresh: Chunk + if s > r.nextChunkSize: + fresh = cast[Chunk](osAllocPages(s)) + else: + fresh = cast[Chunk](osTryAllocPages(r.nextChunkSize)) + if fresh == nil: + fresh = cast[Chunk](osAllocPages(s)) + # lowest bit in totalSize is the "don't increase nextChunkSize" + inc r.totalSize + else: + s = r.nextChunkSize + fresh.size = s + fresh.final = nil + r.totalSize += s + let old = r.last + if old == nil: + r.head = fresh + else: + r.last.next = fresh + r.chunk = fresh +! sizeof(BaseChunk) + r.last = fresh + r.remaining = s - sizeof(BaseChunk) + +proc alloc(r: var MemRegion; size: int): pointer {.inline.} = + if unlikely(r.remaining < size): allocSlowPath(r, size) + dec(r.remaining, size) + result = r.chunk + inc r.chunk, size + +proc runFinalizers(c: Chunk) = + var it = c.head + while it != nil: + # indivually freed objects with finalizer stay in the list, but + # their typ is nil then: + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](cell.typ.finalizer))(cell+!sizeof(ObjHeader)) + it = it.next + +proc dealloc(r: var MemRegion; p: pointer) = + let it = p-!sizeof(ObjHeader) + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](cell.typ.finalizer))(p) + it.typ = nil + +proc deallocAll(head: Chunk) = + var it = head + while it != nil: + runFinalizers(it) + osDeallocPages(it, it.size) + it = it.next + +proc deallocAll*(r: var MemRegion) = + deallocAll(r.head) + zeroMem(addr r, sizeof r) + +proc obstackPtr*(r: MemRegion): StackPtr = + result.chunk = r.chunk + result.remaining = r.remaining + result.current = r.last + +proc setObstackPtr*(r: MemRegion; sp: StackPtr) = + # free everything after 'sp': + if sp.current != nil: + deallocAll(sp.current.next) + r.chunk = sp.chunk + r.remaining = sp.remaining + r.last = sp.current + +proc joinRegion*(dest: var MemRegion; src: MemRegion) = + # merging is not hard. + if dest.head.isNil: + dest.head = src.head + else: + dest.last.next = src.head + dest.last = src.last + dest.chunk = src.chunk + dest.remaining = src.remaining + dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize) + dest.totalSize += src.totalSize + if dest.hole.size < src.hole.size: + dest.hole = src.hole + +proc isOnHeap*(r: MemRegion; p: pointer): bool = + # the last chunk is the largest, so check it first. It's also special + # in that contains the current bump pointer: + if r.last >= p and p < r.chunk: + return true + var it = r.head + while it != r.last: + if it >= p and p <= it+!it.size: return true + it = it.next + +proc isInteriorPointer(r: MemRegion; p: pointer): pointer = + discard " we cannot patch stack pointers anyway!" + +type + PointerStackChunk = object + next, prev: ptr PointerStackChunk + len: int + data: array[128, pointer] + +template head(s: PointerStackChunk): untyped = s.prev +template tail(s: PointerStackChunk): untyped = s.next + +include chains + +proc push(r: var MemRegion; s: var PointerStackChunk; x: pointer) = + if s.len < high(s.data): + s.data[s.len] = x + inc s.len + else: + let fresh = cast[ptr PointerStackChunk](alloc(r, sizeof(PointerStackChunk))) + fresh.len = 1 + fresh.data[0] = x + fresh.next = nil + fresh.prev = nil + append(s, fresh) + + +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, mt: PNimType) {.benign.} +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, n: ptr TNimNode) {.benign.} = + var + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) + case n.kind + of nkSlot: + genericDeepCopyAux(cast[pointer](d +% n.offset), + cast[pointer](s +% n.offset), n.typ) + of nkList: + for i in 0..n.len-1: + genericDeepCopyAux(dest, src, n.sons[i]) + of nkCase: + var dd = selectBranch(dest, n) + 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: + genericResetAux(dest, dd) + copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), + n.typ.size) + if m != nil: + genericDeepCopyAux(dest, src, m) + of nkNone: sysAssert(false, "genericDeepCopyAux") + +proc copyDeepString(dr: var MemRegion; stack: var PointerStackChunk; src: NimString): NimString {.inline.} = + result = rawNewStringNoInit(dr, src.len) + result.len = src.len + c_memcpy(result.data, src.data, src.len + 1) + +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, mt: PNimType) = + var + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) + sysAssert(mt != nil, "genericDeepCopyAux 2") + case mt.kind + of tyString: + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] + if s2 == nil: + x[] = nil + else: + x[] = copyDeepString(cast[NimString](s2)) + of tySequence: + var s2 = cast[PPointer](src)[] + var seq = cast[PGenericSeq](s2) + var x = cast[PPointer](dest) + if s2 == nil: + x[] = nil + return + sysAssert(dest != nil, "genericDeepCopyAux 3") + x[] = newSeq(mt, seq.len) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) + for i in 0..seq.len-1: + genericDeepCopyAux(dr, stack, + cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% + GenericSeqSize), + mt.base) + of tyObject: + # we need to copy m_type field for tyObject, as it could be empty for + # sequence reallocations: + var pint = cast[ptr PNimType](dest) + pint[] = cast[ptr PNimType](src)[] + if mt.base != nil: + genericDeepCopyAux(dr, stack, dest, src, mt.base) + genericDeepCopyAux(dr, stack, dest, src, mt.node) + of tyTuple: + genericDeepCopyAux(dr, stack, dest, src, mt.node) + of tyArray, tyArrayConstr: + for i in 0..(mt.size div mt.base.size)-1: + genericDeepCopyAux(dr, stack, + cast[pointer](d +% i*% mt.base.size), + cast[pointer](s +% i*% mt.base.size), mt.base) + of tyRef: + let s2 = cast[PPointer](src)[] + if s2 == nil: + cast[PPointer](dest)[] = nil + else: + # we modify the header of the cell temporarily; instead of the type + # field we store a forwarding pointer. XXX This is bad when the cloning + # fails due to OOM etc. + let x = usrToCell(s2) + let forw = cast[int](x.typ) + if (forw and 1) == 1: + # we stored a forwarding pointer, so let's use that: + let z = cast[pointer](forw and not 1) + unsureAsgnRef(cast[PPointer](dest), z) + 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(dr, stack, z, s2, realType.base) + x.typ = realType + else: + copyMem(dest, src, mt.size) + +proc joinAliveDataFromRegion*(dest: var MemRegion; src: var MemRegion; + root: pointer): pointer = + # we mark the alive data and copy only alive data over to 'dest'. + # This is O(liveset) but it nicely compacts memory, so it's fine. + # We use the 'typ' field as a forwarding pointer. The forwarding + # pointers have bit 0 set, so we can disambiguate them. + # We allocate a temporary stack in 'src' that we later free: + var s: PointerStackChunk + s.len = 1 + s.data[0] = root + while s.len > 0: + var p: pointer + if s.tail == nil: + p = s.data[s.len-1] + dec s.len + else: + p = s.tail.data[s.tail.len-1] + dec s.tail.len + if s.tail.len == 0: + unlink(s, s.tail) + +proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer = + var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader))) + res.typ = typ + if typ.finalizer != nil: + res.nextFinal = r.chunk.head + r.chunk.head = res + result = res +! sizeof(ObjHeader) + +proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, region) + zeroMem(result, size) + when defined(memProfiler): nimProfile(size) + +proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, region) + when defined(memProfiler): nimProfile(size) + +proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + +proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, gch) + zeroMem(result, size) + +proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + +proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = + collectCT(gch) + var ol = usrToCell(old) + sysAssert(ol.typ != nil, "growObj: 1") + gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") + + var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell))) + var elemSize = 1 + if ol.typ.kind != tyString: elemSize = ol.typ.base.size + + var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize + copyMem(res, ol, oldsize + sizeof(Cell)) + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + newsize-oldsize) + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + result = cellToUsr(res) + +proc growObj(old: pointer, newsize: int): pointer {.rtl.} = + result = growObj(old, newsize, region) + diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index c96f2f958..1b98883b9 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -263,9 +263,7 @@ proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = when defined(nimphp): asm """ - $result = array(); - for($i = 0; $i < `len`; $i++) $result[] = chr(0); - return $result; + return str_repeat(chr(0),`len`); """ else: asm """ diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index e0d14737e..5e576f0a3 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -427,7 +427,7 @@ elif defined(nogc) and defined(useMalloc): proc initGC() = discard proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = - result = alloc(size) + result = alloc0(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index cc1a28213..78410d716 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -94,7 +94,7 @@ elif defined(posix): proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, off: int): pointer {.header: "<sys/mman.h>".} - proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".} + proc munmap(adr: pointer, len: int): cint {.header: "<sys/mman.h>".} proc osAllocPages(size: int): pointer {.inline.} = result = mmap(nil, size, PROT_READ or PROT_WRITE, @@ -108,7 +108,7 @@ elif defined(posix): if result == cast[pointer](-1): result = nil proc osDeallocPages(p: pointer, size: int) {.inline} = - when reallyOsDealloc: munmap(p, size) + when reallyOsDealloc: discard munmap(p, size) elif defined(windows): const diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 7f18ed31c..4da4781ef 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -74,22 +74,22 @@ proc reprChar(x: char): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = # we read an 'int' but this may have been too large, so mask the other bits: - let e = if typ.size == 1: e and 0xff - elif typ.size == 2: e and 0xffff - elif typ.size == 4: e and 0xffffffff - else: e + let b = (sizeof(int)-typ.size)*8 # bits + let m = 1 shl (b-1) # mask + var o = e and ((1 shl b)-1) # clear upper bits + o = (o xor m) - m # sign extend # XXX we need a proper narrowing based on signedness here #e and ((1 shl (typ.size*8)) - 1) if ntfEnumHole notin typ.flags: - if e <% typ.node.len: - return $typ.node.sons[e].name + if o <% typ.node.len: + return $typ.node.sons[o].name else: # ugh we need a slow linear search: var n = typ.node var s = n.sons for i in 0 .. n.len-1: - if s[i].offset == e: return $s[i].name - result = $e & " (invalid data!)" + if s[i].offset == o: return $s[i].name + result = $o & " (invalid data!)" type PByteArray = ptr array[0.. 0xffff, int8] @@ -260,6 +260,7 @@ when not defined(useNimRtl): of tyInt16: add result, $int(cast[ptr int16](p)[]) of tyInt32: add result, $int(cast[ptr int32](p)[]) of tyInt64: add result, $(cast[ptr int64](p)[]) + of tyUInt: add result, $(cast[ptr uint](p)[]) of tyUInt8: add result, $(cast[ptr uint8](p)[]) of tyUInt16: add result, $(cast[ptr uint16](p)[]) of tyUInt32: add result, $(cast[ptr uint32](p)[]) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index e81219a70..d0bba6775 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -82,12 +82,11 @@ when NoFakeVars: const IOFBF = cint(0) IONBF = cint(4) - elif defined(macosx) or defined(linux): + else: + # On all systems I could find, including Linux, Mac OS X, and the BSDs const IOFBF = cint(0) IONBF = cint(2) - else: - {.error: "IOFBF not ported to your platform".} else: var IOFBF {.importc: "_IOFBF", nodecl.}: cint @@ -271,12 +270,33 @@ const # we always use binary here as for Nim the OS line ending # should not be translated. +when defined(posix) and not defined(nimscript): + type + Mode {.importc: "mode_t", header: "<sys/types.h>".} = cint + + Stat {.importc: "struct stat", + header: "<sys/stat.h>", final, pure.} = object ## struct stat + st_mode: Mode ## Mode of file + + proc S_ISDIR(m: Mode): bool {.importc, header: "<sys/stat.h>".} + ## Test for a directory. + + proc fstat(a1: cint, a2: var Stat): cint {.importc, header: "<sys/stat.h>".} proc open(f: var File, filename: string, mode: FileMode = fmRead, bufSize: int = -1): bool = var p: pointer = fopen(filename, FormatOpen[mode]) if p != nil: + when defined(posix) and not defined(nimscript): + # How `fopen` handles opening a directory is not specified in ISO C and + # POSIX. We do not want to handle directories as regular files that can + # be opened. + var f2 = cast[File](p) + var res: Stat + if fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode): + close(f2) + return false result = true f = cast[File](p) if bufSize > 0 and bufSize <= high(cint).int: diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index fbae46760..5e899dd1a 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -412,7 +412,7 @@ const FD_SETSIZE* = 64 MSG_PEEK* = 2 - INADDR_ANY* = 0 + INADDR_ANY* = 0'u32 INADDR_LOOPBACK* = 0x7F000001 INADDR_BROADCAST* = -1 INADDR_NONE* = -1 @@ -441,12 +441,12 @@ type sa_data: array[0..13, char] InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object - s_addr*: int32 # IP address + s_addr*: uint32 # IP address Sockaddr_in* {.importc: "SOCKADDR_IN", header: "winsock2.h".} = object sin_family*: int16 - sin_port*: int16 # unsigned + sin_port*: uint16 sin_addr*: InAddr sin_zero*: array[0..7, char] @@ -456,7 +456,7 @@ type Sockaddr_in6* {.importc: "SOCKADDR_IN6", header: "ws2tcpip.h".} = object sin6_family*: int16 - sin6_port*: int16 # unsigned + sin6_port*: uint16 sin6_flowinfo*: int32 # unsigned sin6_addr*: In6_addr sin6_scope_id*: int32 # unsigned @@ -590,7 +590,7 @@ proc getnameinfo*(a1: ptr SockAddr, a2: SockLen, a6: SockLen, a7: cint): cint {. stdcall, importc: "getnameinfo", dynlib: ws2dll.} -proc inet_addr*(cp: cstring): int32 {. +proc inet_addr*(cp: cstring): uint32 {. stdcall, importc: "inet_addr", dynlib: ws2dll.} proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {. @@ -833,9 +833,9 @@ type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring, var inet_ntop_real: inet_ntop_proc = nil -let L = loadLib(ws2dll) -if L != nil: - inet_ntop_real = cast[inet_ntop_proc](symAddr(L, "inet_ntop")) +let ws2 = loadLib(ws2dll) +if ws2 != nil: + inet_ntop_real = cast[inet_ntop_proc](symAddr(ws2, "inet_ntop")) proc WSAAddressToStringA(pAddr: ptr SockAddr, addrSize: DWORD, unused: pointer, pBuff: cstring, pBuffSize: ptr DWORD): cint {.stdcall, importc, dynlib: ws2dll.} proc inet_ntop_emulated(family: cint, paddr: pointer, pStringBuffer: cstring, |