summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/core/macros.nim2
-rw-r--r--lib/impure/db_postgres.nim182
-rw-r--r--lib/impure/re.nim2
-rw-r--r--lib/pure/algorithm.nim2
-rw-r--r--lib/pure/asynchttpserver.nim4
-rw-r--r--lib/pure/collections/heapqueue.nim41
-rw-r--r--lib/pure/distros.nim2
-rw-r--r--lib/pure/hashes.nim10
-rw-r--r--lib/pure/httpcore.nim6
-rw-r--r--lib/pure/json.nim9
-rw-r--r--lib/pure/logging.nim2
-rw-r--r--lib/pure/os.nim6
-rw-r--r--lib/pure/parseutils.nim24
-rw-r--r--lib/pure/strutils.nim4
-rw-r--r--lib/pure/unittest.nim2
-rw-r--r--lib/system.nim12
-rw-r--r--lib/system/alloc.nim5
-rw-r--r--lib/system/gc_common.nim29
-rw-r--r--lib/system/sysio.nim19
-rw-r--r--lib/system/threads.nim16
20 files changed, 308 insertions, 71 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 3adf4670d..83776f16b 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -742,6 +742,8 @@ proc `$`*(node: NimNode): string {.compileTime.} =
     result = $node.symbol
   of nnkOpenSymChoice, nnkClosedSymChoice:
     result = $node[0]
+  of nnkAccQuoted:
+    result = $node[0]
   else:
     badNodeKind node.kind, "$"
 
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index 60bd1f081..fc587b5df 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -69,16 +69,14 @@ import db_common
 export db_common
 
 type
-  DbConn* = PPGconn   ## encapsulates a database connection
-  Row* = seq[string]  ## a row of a dataset. NULL database values will be
-                      ## converted to nil.
-  InstantRow* = tuple[res: PPGresult, line: int32]  ## a handle that can be
-                                                    ## used to get a row's
-                                                    ## column text on demand
+  DbConn* = PPGconn    ## encapsulates a database connection
+  Row* = seq[string]   ## a row of a dataset. NULL database values will be
+                       ## converted to nil.
+  InstantRow* = object ## a handle that can be
+    res: PPGresult     ## used to get a row's
+    line: int          ## column text on demand             
   SqlPrepared* = distinct string ## a identifier for the prepared queries
-
-{.deprecated: [TRow: Row, TDbConn: DbConn,
-              TSqlPrepared: SqlPrepared].}
+{.deprecated: [TRow: Row, TDbConn: DbConn, TSqlPrepared: SqlPrepared].}
 
 proc dbError*(db: DbConn) {.noreturn.} =
   ## raises a DbError exception.
@@ -213,7 +211,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
   ## on demand using []. Returned handle is valid only within iterator body.
   var res = setupQuery(db, query, args)
   for i in 0..pqNtuples(res)-1:
-    yield (res: res, line: i)
+    yield InstantRow(res: res, line: i)
   pqClear(res)
 
 iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
@@ -223,16 +221,170 @@ iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
   ## on demand using []. Returned handle is valid only within iterator body.
   var res = setupQuery(db, stmtName, args)
   for i in 0..pqNtuples(res)-1:
-    yield (res: res, line: i)
+    yield InstantRow(res: res, line: i)
+  pqClear(res)
+
+proc getColumnType(res: PPGresult, col: int) : DbType =
+  ## returns DbType for given column in the row
+  ## defined in pg_type.h file in the postgres source code
+  ## Wire representation for types: http://www.npgsql.org/dev/types.html
+  var oid = pqftype(res, int32(col))
+  ## The integer returned is the internal OID number of the type
+  case oid
+  of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool")
+  of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea")
+
+  of 21:   return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2)
+  of 23:   return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4)
+  of 20:   return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8)
+  of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit")  
+  of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit")
+
+  of 18:   return DbType(kind: DbTypeKind.dbFixedChar, name: "char")
+  of 19:   return DbType(kind: DbTypeKind.dbFixedChar, name: "name")
+  of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar")
+
+  of 25:   return DbType(kind: DbTypeKind.dbVarchar, name: "text")
+  of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar")
+  of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring")
+
+  of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4")
+  of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8")
+
+  of 790:  return DbType(kind: DbTypeKind.dbDecimal, name: "money")  
+  of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric")
+
+  of 704:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval")
+  of 702:  return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime")
+  of 703:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime")
+  of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date")
+  of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time")
+  of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp")
+  of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz")
+  of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval")
+  of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz")
+
+  of 114:  return DbType(kind: DbTypeKind.dbJson, name: "json")
+  of 142:  return DbType(kind: DbTypeKind.dbXml, name: "xml")
+  of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb")
+
+  of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point")
+  of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg")
+  of 602: return DbType(kind: DbTypeKind.dbPath, name: "path")
+  of 603: return DbType(kind: DbTypeKind.dbBox, name: "box")
+  of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon")
+  of 628: return DbType(kind: DbTypeKind.dbLine, name: "line")
+  of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle")  
+
+  of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr")
+  of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr")
+  of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet")
+  
+  of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid")
+  of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector")
+  of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery")
+  of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot")
+
+  of 27:   return DbType(kind: DbTypeKind.dbComposite, name: "tid")
+  of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor")
+  of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record")
+  of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range")
+  of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange")
+  of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange")
+  of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange")
+  of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange")
+  of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range")
+
+  of 22:   return DbType(kind: DbTypeKind.dbArray, name: "int2vector")
+  of 30:   return DbType(kind: DbTypeKind.dbArray, name: "oidvector")
+  of 143:  return DbType(kind: DbTypeKind.dbArray, name: "xml[]")
+  of 199:  return DbType(kind: DbTypeKind.dbArray, name: "json[]")
+  of 629:  return DbType(kind: DbTypeKind.dbArray, name: "line[]")
+  of 651:  return DbType(kind: DbTypeKind.dbArray, name: "cidr[]")
+  of 719:  return DbType(kind: DbTypeKind.dbArray, name: "circle[]")
+  of 791:  return DbType(kind: DbTypeKind.dbArray, name: "money[]")
+  of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]")
+  of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]")
+  of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]")
+  of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]")
+  of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]")
+  of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]")
+  of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]")
+  of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]")
+  of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]")
+  of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]")
+  of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]")
+  of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]")
+  of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]")
+  of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]")
+  of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]")
+  of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]")
+  of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]")
+  of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]")
+  of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]")
+  of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]")
+  of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]")
+  of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]")
+  of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]")
+  of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]")
+  of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]")
+  of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]")
+  of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]")
+  of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]")
+  of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]")
+  of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]")
+  of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]")
+  of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]")
+  of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]")
+  of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]")
+  of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]")
+  of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]")
+  of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]")
+  of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]")
+  of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]")
+  of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]")
+  of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]")
+  of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]")
+  of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]")
+  of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]")
+  of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]")
+  of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]")
+  of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]")
+  of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]")
+  of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]")
+  of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]")
+  of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]")
+  of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]")
+
+  of 705:  return DbType(kind: DbTypeKind.dbUnknown, name: "unknown")
+  else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced.
+
+proc setColumnInfo(columns: var DbColumns; res: PPGresult; L: int32) =
+  setLen(columns, L)
+  for i in 0..<L:
+    columns[i].name = $pqfname(res, i)
+    columns[i].typ = getColumnType(res, i)
+    columns[i].tableName = $(pqftable(res, i)) ## Returns the OID of the table from which the given column was fetched.
+                                               ## Query the system table pg_class to determine exactly which table is referenced.  
+    #columns[i].primaryKey = libpq does not have a function for that
+    #columns[i].foreignKey = libpq does not have a function for that
+
+iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [ReadDbEffect].} =
+  var res = setupQuery(db, query, args)
+  setColumnInfo(columns, res, pqnfields(res))
+  for i in 0..<pqntuples(res):
+    yield InstantRow(res: res, line: i)
   pqClear(res)
 
-proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
+proc `[]`*(row: InstantRow; col: int): string {.inline.} =
   ## returns text for given column of the row
-  $pqgetvalue(row.res, row.line, col)
+  $pqgetvalue(row.res, int32(row.line), int32(col))
 
-proc len*(row: InstantRow): int32 {.inline.} =
+proc len*(row: InstantRow): int {.inline.} =
   ## returns number of columns in the row
-  pqNfields(row.res)
+  int(pqNfields(row.res))
 
 proc getRow*(db: DbConn, query: SqlQuery,
              args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 9d5d2bdd0..e00f91de1 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -290,7 +290,7 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)-1)
+    if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
     else: matches[i-1] = nil
   return rawMatches[0]
 
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 739cdc16b..bc39f153b 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -46,7 +46,7 @@ proc reverse*[T](a: var openArray[T], first, last: Natural) =
 
 proc reverse*[T](a: var openArray[T]) =
   ## reverses the array `a`.
-  reverse(a, 0, a.high)
+  reverse(a, 0, max(0, a.high))
 
 proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] =
   ## returns the reverse of the array `a[first..last]`.
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index a558d9d7e..672eb34a0 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -31,7 +31,7 @@
 ##
 ##    waitFor server.serve(Port(8080), cb)
 
-import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
+import tables, asyncnet, asyncdispatch, parseutils, uri, strutils, nativesockets
 import httpcore
 
 export httpcore except parseHeader
@@ -241,7 +241,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
   ## specified address and port.
   ##
   ## When a request is made by a client the specified callback will be called.
-  server.socket = newAsyncSocket()
+  server.socket = newAsyncSocket(AF_INET6)
   if server.reuseAddr:
     server.socket.setSockOpt(OptReuseAddr, true)
   if server.reusePort:
diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim
index abe20e556..f86ba1d3f 100644
--- a/lib/pure/collections/heapqueue.nim
+++ b/lib/pure/collections/heapqueue.nim
@@ -77,8 +77,10 @@ proc pop*[T](heap: var HeapQueue[T]): T =
 proc del*[T](heap: var HeapQueue[T], index: int) =
   ## Removes element at `index`, maintaining the heap invariant.
   swap(seq[T](heap)[^1], seq[T](heap)[index])
-  seq[T](heap).setLen(heap.len - 1)
-  heap.siftup(index)
+  let newLen = heap.len - 1
+  seq[T](heap).setLen(newLen)
+  if index < newLen:
+    heap.siftup(index)
 
 proc replace*[T](heap: var HeapQueue[T], item: T): T =
   ## Pop and return the current smallest value, and add the new item.
@@ -101,16 +103,19 @@ proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
   return item
 
 when isMainModule:
+  proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
+    var tmp = h
+    result = @[]
+    while tmp.len > 0:
+      result.add(pop(tmp))
+
   block: # Simple sanity test
     var heap = newHeapQueue[int]()
     let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
     for item in data:
       push(heap, item)
     doAssert(heap[0] == 0)
-    var sort = newSeq[int]()
-    while heap.len > 0:
-      sort.add(pop(heap))
-    doAssert(sort == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+    doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
 
   block: # Test del
     var heap = newHeapQueue[int]()
@@ -121,11 +126,27 @@ when isMainModule:
     doAssert(heap[0] == 1)
 
     heap.del(seq[int](heap).find(7))
+    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
+
     heap.del(seq[int](heap).find(5))
+    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
+
     heap.del(seq[int](heap).find(6))
+    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
+
     heap.del(seq[int](heap).find(2))
+    doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
+
+  block: # Test del last
+    var heap = newHeapQueue[int]()
+    let data = [1, 2, 3]
+    for item in data: push(heap, item)
+
+    heap.del(2)
+    doAssert(heap.toSortedSeq == @[1, 2])
 
-    var sort = newSeq[int]()
-    while heap.len > 0:
-      sort.add(pop(heap))
-    doAssert(sort == @[1, 3, 4, 8, 9])
+    heap.del(1)
+    doAssert(heap.toSortedSeq == @[1])
+
+    heap.del(0)
+    doAssert(heap.toSortedSeq == @[])
diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim
index ff30f6134..896497630 100644
--- a/lib/pure/distros.nim
+++ b/lib/pure/distros.nim
@@ -225,7 +225,7 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
     else:
       result = ("<your package manager here> install " & p, true)
   else:
-    result = ("brew install " & p, true)
+    result = ("brew install " & p, false)
 
 proc foreignDep*(foreignPackageName: string) =
   ## Registers 'foreignPackageName' to the internal list of foreign deps.
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 11af81149..17d1c6442 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -127,6 +127,15 @@ proc hash*(x: string): Hash =
     h = h !& ord(x[i])
   result = !$h
 
+proc hash*(x: cstring): Hash =
+  ## efficient hashing of null-terminated strings
+  var h: Hash = 0
+  var i = 0
+  while x[i] != 0.char:
+    h = h !& ord(x[i])
+    inc i
+  result = !$h
+
 proc hash*(sBuf: string, sPos, ePos: int): Hash =
   ## efficient hashing of a string buffer, from starting
   ## position `sPos` to ending position `ePos`
@@ -239,6 +248,7 @@ proc hash*[A](x: set[A]): Hash =
 
 when isMainModule:
   doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) )
+  doAssert( hash("aa bb aaaa1234") == hash(cstring("aa bb aaaa1234")) )
   doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") )
   doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") )
   let xx = @['H','e','l','l','o']
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index d7f720f66..aa8f1958d 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -206,6 +206,8 @@ proc parseHeader*(line: string): tuple[key: string, value: seq[string]] =
   inc(i) # skip :
   if i < len(line):
     i += parseList(line, result.value, i)
+  elif result.key.len > 0:
+    result.value = @[""]
   else:
     result.value = @[]
 
@@ -318,4 +320,6 @@ when isMainModule:
   let (key, value) = parseHeader("foobar: ")
   test = newHttpHeaders()
   test[key] = value
-  doAssert test["foobar"] == ""
\ No newline at end of file
+  doAssert test["foobar"] == ""
+
+  doAssert parseHeader("foobar:") == ("foobar", @[""])
\ No newline at end of file
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 5e36a2aa1..c7b581a85 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -952,7 +952,7 @@ proc newIndent(curr, indent: int, ml: bool): int =
   else: return indent
 
 proc nl(s: var string, ml: bool) =
-  if ml: s.add("\n")
+  s.add(if ml: "\n" else: " ")
 
 proc escapeJson*(s: string; result: var string) =
   ## Converts a string `s` to its JSON representation.
@@ -986,15 +986,14 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
               lstArr = false, currIndent = 0) =
   case node.kind
   of JObject:
-    if currIndent != 0 and not lstArr: result.nl(ml)
-    result.indent(currIndent) # Indentation
+    if lstArr: result.indent(currIndent) # Indentation
     if node.fields.len > 0:
       result.add("{")
       result.nl(ml) # New line
       var i = 0
       for key, val in pairs(node.fields):
         if i > 0:
-          result.add(", ")
+          result.add(",")
           result.nl(ml) # New Line
         inc i
         # Need to indent more than {
@@ -1030,7 +1029,7 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
       result.nl(ml)
       for i in 0..len(node.elems)-1:
         if i > 0:
-          result.add(", ")
+          result.add(",")
           result.nl(ml) # New Line
         toPretty(result, node.elems[i], indent, ml,
             true, newIndent(currIndent, indent, ml))
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 5544a4b3f..65724f75a 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -46,6 +46,8 @@
 ##
 ## **Warning:** The global list of handlers is a thread var, this means that
 ## the handlers must be re-added in each thread.
+## **Warning:** When logging on disk or console, only error and fatal messages
+## are flushed out immediately. Use flushFile() where needed.
 
 import strutils, times
 when not defined(js):
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 4cd3870c7..6bf776a44 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1002,8 +1002,8 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
 
 iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
   tags: [ReadDirEffect].} =
-  ## walks over the directory `dir` and yields for each file in `dir`. The
-  ## full path for each file is returned.
+  ## Recursively walks over the directory `dir` and yields for each file in `dir`.
+  ## The full path for each file is returned. Directories are not returned.
   ## **Warning**:
   ## Modifying the directory structure while the iterator
   ## is traversing may result in undefined behavior!
@@ -1185,7 +1185,7 @@ proc createHardlink*(src, dest: string) =
 
 proc parseCmdLine*(c: string): seq[string] {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a command line into several components;
+  ## Splits a `command line`:idx: into several components;
   ## This proc is only occasionally useful, better use the `parseopt` module.
   ##
   ## On Windows, it uses the following parsing rules
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index fb7d72182..8d53a0360 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -250,18 +250,18 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
   elif result != 0:
     number = int(res)
 
-# overflowChecks doesn't work with uint64
-proc rawParseUInt(s: string, b: var uint64, start = 0): int =
+# overflowChecks doesn't work with BiggestUInt
+proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
   var
-    res = 0'u64
-    prev = 0'u64
+    res = 0.BiggestUInt
+    prev = 0.BiggestUInt
     i = start
   if s[i] == '+': inc(i) # Allow
   if s[i] in {'0'..'9'}:
     b = 0
     while s[i] in {'0'..'9'}:
       prev = res
-      res = res * 10 + (ord(s[i]) - ord('0')).uint64
+      res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
       if prev > res:
         return 0 # overflowChecks emulation
       inc(i)
@@ -269,13 +269,13 @@ proc rawParseUInt(s: string, b: var uint64, start = 0): int =
     b = res
     result = i - start
 
-proc parseBiggestUInt*(s: string, number: var uint64, start = 0): int {.
+proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
   rtl, extern: "npuParseBiggestUInt", noSideEffect.} =
   ## parses an unsigned integer starting at `start` and stores the value
   ## into `number`.
   ## Result is the number of processed chars or 0 if there is no integer
   ## or overflow detected.
-  var res: uint64
+  var res: BiggestUInt
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
   result = rawParseUInt(s, res, start)
@@ -287,12 +287,12 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {.
   ## into `number`.
   ## Result is the number of processed chars or 0 if there is no integer or
   ## overflow detected.
-  var res: uint64
+  var res: BiggestUInt
   result = parseBiggestUInt(s, res, start)
-  if (sizeof(uint) <= 4) and
-      (res > 0xFFFF_FFFF'u64):
-    raise newException(OverflowError, "overflow")
-  elif result != 0:
+  when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
+    if res > 0xFFFF_FFFF'u64:
+      raise newException(OverflowError, "overflow")
+  if result != 0:
     number = uint(res)
 
 proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 235e66f6a..9c9da92c6 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -898,7 +898,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
 
 proc toHex*[T](x: T): string =
   ## Shortcut for ``toHex(x, T.sizeOf * 2)``
-  toHex(x, T.sizeOf * 2)
+  toHex(BiggestInt(x), T.sizeOf * 2)
 
 proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
   rtl, extern: "nsuIntToStr".} =
@@ -939,7 +939,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, procvar,
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid unsigned integer: " & s)
 
-proc parseBiggestUInt*(s: string): uint64 {.noSideEffect, procvar,
+proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar,
   rtl, extern: "nsuParseBiggestUInt".} =
   ## Parses a decimal unsigned integer value contained in `s`.
   ##
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index cdca02ed7..01af0f839 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -24,7 +24,7 @@
 ##       echo "run before each test"
 ##
 ##     teardown:
-##       echo "run after each test":
+##       echo "run after each test"
 ##
 ##     test "essential truths":
 ##       # give up and stop if this fails
diff --git a/lib/system.nim b/lib/system.nim
index 13ca6eaf7..6388e278e 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1512,6 +1512,17 @@ type
     ## compiler supports. Currently this is ``float64``, but it is
     ## platform-dependant in general.
 
+when defined(JS):
+  type BiggestUInt* = uint32
+    ## is an alias for the biggest unsigned integer type the Nim compiler
+    ## supports. Currently this is ``uint32`` for JS and ``uint64`` for other
+    ## targets.
+else:
+  type BiggestUInt* = uint64
+    ## is an alias for the biggest unsigned integer type the Nim compiler
+    ## supports. Currently this is ``uint32`` for JS and ``uint64`` for other
+    ## targets.
+
 {.deprecated: [TAddress: ByteAddress].}
 
 when defined(windows):
@@ -2732,6 +2743,7 @@ when not defined(JS): #and not defined(nimscript):
       # we use binary mode on Windows:
       c_setmode(c_fileno(stdin), O_BINARY)
       c_setmode(c_fileno(stdout), O_BINARY)
+      c_setmode(c_fileno(stderr), O_BINARY)
 
     when defined(endb):
       proc endbStep()
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index a124d7537..f28a124d2 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -292,10 +292,15 @@ proc writeFreeList(a: MemRegion) =
               it, it.next, it.prev, it.size)
     it = it.next
 
+const nimMaxHeap {.intdefine.} = 0
+
 proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
   when not defined(emscripten):
     if not a.blockChunkSizeIncrease:
       let usedMem = a.currMem # - a.freeMem
+      when nimMaxHeap != 0:
+        if usedMem > nimMaxHeap * 1024 * 1024:
+          raiseOutOfMem()
       if usedMem < 64 * 1024:
         a.nextChunkSize = PageSize*4
       else:
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 269514ceb..6ab6bd920 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -128,13 +128,7 @@ iterator items(stack: ptr GcStack): ptr GcStack =
     yield s
     s = s.next
 
-# There will be problems with GC in foreign threads if `threads` option is off or TLS emulation is enabled
-const allowForeignThreadGc = compileOption("threads") and not compileOption("tlsEmulation")
-
-when allowForeignThreadGc:
-  var
-    localGcInitialized {.rtlThreadVar.}: bool
-
+when declared(threadType):
   proc setupForeignThreadGc*() {.gcsafe.} =
     ## Call this if you registered a callback that will be run from a thread not
     ## under your control. This has a cheap thread-local guard, so the GC for
@@ -143,16 +137,33 @@ when allowForeignThreadGc:
     ##
     ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off``
     ## switches are used
-    if not localGcInitialized:
-      localGcInitialized = true
+    if threadType == ThreadType.None:
       initAllocator()
       var stackTop {.volatile.}: pointer
       setStackBottom(addr(stackTop))
       initGC()
+      threadType = ThreadType.ForeignThread
+
+  proc tearDownForeignThreadGc*() {.gcsafe.} =
+    ## Call this to tear down the GC, previously initialized by ``setupForeignThreadGc``.
+    ## If GC has not been previously initialized, or has already been torn down, the
+    ## call does nothing.
+    ##
+    ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off``
+    ## switches are used
+    if threadType != ThreadType.ForeignThread:
+      return
+    when declared(deallocOsPages): deallocOsPages()
+    threadType = ThreadType.None
+    when declared(gch): zeroMem(addr gch, sizeof(gch))
+
 else:
   template setupForeignThreadGc*() =
     {.error: "setupForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".}
 
+  template tearDownForeignThreadGc*() =
+    {.error: "tearDownForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".}
+
 # ----------------- stack management --------------------------------------
 #  inspired from Smart Eiffel
 
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 115b67c18..7444661e3 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -171,21 +171,23 @@ proc readLine(f: File): TaintedString =
 
 proc write(f: File, i: int) =
   when sizeof(int) == 8:
-    c_fprintf(f, "%lld", i)
+    if c_fprintf(f, "%lld", i) < 0: checkErr(f)
   else:
-    c_fprintf(f, "%ld", i)
+    if c_fprintf(f, "%ld", i) < 0: checkErr(f)
 
 proc write(f: File, i: BiggestInt) =
   when sizeof(BiggestInt) == 8:
-    c_fprintf(f, "%lld", i)
+    if c_fprintf(f, "%lld", i) < 0: checkErr(f)
   else:
-    c_fprintf(f, "%ld", i)
+    if c_fprintf(f, "%ld", i) < 0: checkErr(f)
 
 proc write(f: File, b: bool) =
   if b: write(f, "true")
   else: write(f, "false")
-proc write(f: File, r: float32) = c_fprintf(f, "%g", r)
-proc write(f: File, r: BiggestFloat) = c_fprintf(f, "%g", r)
+proc write(f: File, r: float32) =
+  if c_fprintf(f, "%g", r) < 0: checkErr(f)
+proc write(f: File, r: BiggestFloat) =
+  if c_fprintf(f, "%g", r) < 0: checkErr(f)
 
 proc write(f: File, c: char) = discard c_putc(ord(c), f)
 proc write(f: File, a: varargs[string, `$`]) =
@@ -213,7 +215,10 @@ proc rawFileSize(file: File): int =
   discard c_fseek(file, clong(oldPos), 0)
 
 proc endOfFile(f: File): bool =
-  result = c_feof(f) != 0
+  var c = c_fgetc(f)
+  discard c_ungetc(c, f)
+  return c < 0'i32
+  #result = c_feof(f) != 0
 
 proc readAllFile(file: File, len: int): string =
   # We acquire the filesize beforehand and hope it doesn't change.
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index e8b34bf2e..6e58638e9 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -285,7 +285,19 @@ when useStackMaskHack:
 when not defined(useNimRtl):
   when not useStackMaskHack:
     #when not defined(createNimRtl): initStackBottom()
-    when declared(initGC): initGC()
+    when declared(initGC):
+      initGC()
+      when not emulatedThreadVars:
+        type ThreadType {.pure.} = enum
+          None = 0,
+          NimThread = 1,
+          ForeignThread = 2
+        var
+          threadType {.rtlThreadVar.}: ThreadType
+
+        threadType = ThreadType.NimThread
+
+
 
   when emulatedThreadVars:
     if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
@@ -442,6 +454,8 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
       # init the GC for refc/markandsweep
       setStackBottom(addr(p))
       initGC()
+      when declared(threadType):
+        threadType = ThreadType.NimThread
     when declared(registerThread):
       thrd.stackBottom = addr(thrd)
       registerThread(thrd)