diff options
-rw-r--r-- | lib/pure/concurrency/threadpool.nim | 117 | ||||
-rw-r--r-- | lib/pure/hashes.nim | 131 | ||||
-rw-r--r-- | lib/pure/md5.nim | 107 | ||||
-rw-r--r-- | lib/pure/typetraits.nim | 36 | ||||
-rw-r--r-- | lib/system/channels.nim | 40 | ||||
-rw-r--r-- | lib/system/threads.nim | 71 |
6 files changed, 325 insertions, 177 deletions
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 4846c610f..15dbb10de 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -8,6 +8,12 @@ # ## Implements Nim's `spawn <manual.html#parallel-amp-spawn>`_. +## +## **See also:** +## * `threads module <threads.html>`_ +## * `chanels module <channels.html>`_ +## * `locks module <locks.html>`_ +## * `asyncdispatch module <asyncdispatch.html>`_ when not compileOption("threads"): {.error: "Threadpool requires --threads:on option.".} @@ -53,7 +59,7 @@ type cacheAlign: array[CacheLineSize-4*sizeof(int), byte] left: int cacheAlign2: array[CacheLineSize-sizeof(int), byte] - interest: bool ## wether the master is interested in the "all done" event + interest: bool # whether the master is interested in the "all done" event proc barrierEnter(b: ptr Barrier) {.compilerProc, inline.} = # due to the signaling between threads, it is ensured we are the only @@ -93,11 +99,10 @@ type cv: Semaphore idx: int - FlowVarBase* = ref FlowVarBaseObj ## untyped base class for 'FlowVar[T]' + FlowVarBase* = ref FlowVarBaseObj ## Untyped base class for ``FlowVar[T]``. FlowVarBaseObj = object of RootObj ready, usesSemaphore, awaited: bool - cv: Semaphore #\ - # for 'blockUntilAny' support + cv: Semaphore # for 'blockUntilAny' support ai: ptr AwaitInfo idx: int data: pointer # we incRef and unref it to keep it alive; note this MUST NOT @@ -107,7 +112,7 @@ type FlowVarObj[T] = object of FlowVarBaseObj blob: T - FlowVar*{.compilerProc.}[T] = ref FlowVarObj[T] ## a data flow variable + FlowVar*{.compilerProc.}[T] = ref FlowVarObj[T] ## A data flow variable. ToFreeQueue = object len: int @@ -129,8 +134,9 @@ type readyForTask: Semaphore proc blockUntil*(fv: FlowVarBase) = - ## waits until the value for the flowVar arrives. Usually it is not necessary - ## to call this explicitly. + ## Waits until the value for the ``fv`` arrives. + ## + ## Usually it is not necessary to call this explicitly. if fv.usesSemaphore and not fv.awaited: fv.awaited = true blockUntil(fv.cv) @@ -216,10 +222,12 @@ proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} = signal(fv.cv) proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) = - ## blocks until the ``fv`` is available and then passes its value - ## to ``action``. Note that due to Nim's parameter passing semantics this - ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can - ## sometimes be more efficient than ``^``. + ## Blocks until the ``fv`` is available and then passes its value + ## to ``action``. + ## + ## Note that due to Nim's parameter passing semantics this + ## means that ``T`` doesn't need to be copied so ``awaitAndThen`` can + ## sometimes be more efficient than `^ proc <#^,FlowVar[T]>`_. blockUntil(fv) when T is string or T is seq: action(cast[T](fv.data)) @@ -230,18 +238,18 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) = finished(fv) proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T = - ## blocks until the value is available and then returns this value. + ## Blocks until the value is available and then returns this value. blockUntil(fv) result = cast[ptr T](fv.data) proc `^`*[T](fv: FlowVar[ref T]): ref T = - ## blocks until the value is available and then returns this value. + ## Blocks until the value is available and then returns this value. blockUntil(fv) let src = cast[ref T](fv.data) deepCopy result, src proc `^`*[T](fv: FlowVar[T]): T = - ## blocks until the value is available and then returns this value. + ## Blocks until the value is available and then returns this value. blockUntil(fv) when T is string or T is seq: # XXX closures? deepCopy? @@ -250,11 +258,14 @@ proc `^`*[T](fv: FlowVar[T]): T = result = fv.blob proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int = - ## awaits any of the given flowVars. Returns the index of one flowVar for - ## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at - ## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second - ## call will only blockUntil 'c'. If there is no flowVar left to be able to wait - ## on, -1 is returned. + ## Awaits any of the given ``flowVars``. Returns the index of one ``flowVar`` + ## for which a value arrived. + ## + ## A ``flowVar`` only supports one call to ``blockUntilAny`` at the same time. + ## That means if you ``blockUntilAny([a,b])`` and ``blockUntilAny([b,c])`` + ## the second call will only block until ``c``. If there is no ``flowVar`` left + ## to be able to wait on, -1 is returned. + ## ## **Note**: This results in non-deterministic behaviour and should be avoided. var ai: AwaitInfo ai.cv.initSemaphore() @@ -278,7 +289,7 @@ proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int = proc isReady*(fv: FlowVarBase): bool = ## Determines whether the specified ``FlowVarBase``'s value is available. ## - ## If ``true`` awaiting ``fv`` will not block. + ## If ``true``, awaiting ``fv`` will not block. if fv.usesSemaphore and not fv.awaited: acquire(fv.cv.L) result = fv.cv.counter > 0 @@ -291,9 +302,9 @@ proc nimArgsPassingDone(p: pointer) {.compilerProc.} = signal(w.taskStarted) const - MaxThreadPoolSize* = 256 ## maximal size of the thread pool. 256 threads + MaxThreadPoolSize* = 256 ## Maximum size of the thread pool. 256 threads ## should be good enough for anybody ;-) - MaxDistinguishedThread* = 32 ## maximal number of "distinguished" threads. + MaxDistinguishedThread* = 32 ## Maximum number of "distinguished" threads. type ThreadId* = range[0..MaxDistinguishedThread-1] @@ -368,12 +379,12 @@ when defined(nimPinToCpu): var gCpus: Natural proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) = - ## sets the minimal thread pool size. The default value of this is 4. + ## Sets the minimum thread pool size. The default value of this is 4. minPoolSize = size proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) = - ## sets the maximal thread pool size. The default value of this - ## is ``MaxThreadPoolSize``. + ## Sets the maximum thread pool size. The default value of this + ## is ``MaxThreadPoolSize`` (256). maxPoolSize = size if currentPoolSize > maxPoolSize: for i in maxPoolSize..currentPoolSize-1: @@ -413,37 +424,46 @@ proc setup() = for i in 0..<currentPoolSize: activateWorkerThread(i) proc preferSpawn*(): bool = - ## Use this proc to determine quickly if a 'spawn' or a direct call is - ## preferable. If it returns 'true' a 'spawn' may make sense. In general - ## it is not necessary to call this directly; use 'spawnX' instead. + ## Use this proc to determine quickly if a ``spawn`` or a direct call is + ## preferable. + ## + ## If it returns ``true``, a ``spawn`` may make sense. In general + ## it is not necessary to call this directly; use `spawnX template + ## <#spawnX.t>`_ instead. result = gSomeReady.counter > 0 proc spawn*(call: typed): void {.magic: "Spawn".} - ## always spawns a new task, so that the 'call' is never executed on - ## the calling thread. 'call' has to be proc call 'p(...)' where 'p' - ## is gcsafe and has a return type that is either 'void' or compatible - ## with ``FlowVar[T]``. + ## Always spawns a new task, so that the ``call`` is never executed on + ## the calling thread. + ## + ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a + ## return type that is either ``void`` or compatible with ``FlowVar[T]``. proc pinnedSpawn*(id: ThreadId; call: typed): void {.magic: "Spawn".} - ## always spawns a new task on the worker thread with ``id``, so that - ## the 'call' is **always** executed on - ## the thread. 'call' has to be proc call 'p(...)' where 'p' - ## is gcsafe and has a return type that is either 'void' or compatible - ## with ``FlowVar[T]``. + ## Always spawns a new task on the worker thread with ``id``, so that + ## the ``call`` is **always** executed on the thread. + ## + ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a + ## return type that is either ``void`` or compatible with ``FlowVar[T]``. template spawnX*(call): void = - ## spawns a new task if a CPU core is ready, otherwise executes the - ## call in the calling thread. Usually it is advised to - ## use 'spawn' in order to not block the producer for an unknown - ## amount of time. 'call' has to be proc call 'p(...)' where 'p' - ## is gcsafe and has a return type that is either 'void' or compatible - ## with ``FlowVar[T]``. + ## Spawns a new task if a CPU core is ready, otherwise executes the + ## call in the calling thread. + ## + ## Usually it is advised to use `spawn proc <#spawn,typed>`_ in order to + ## not block the producer for an unknown amount of time. + ## + ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a + ## return type that is either 'void' or compatible with ``FlowVar[T]``. (if preferSpawn(): spawn call else: call) proc parallel*(body: untyped) {.magic: "Parallel".} - ## a parallel section can be used to execute a block in parallel. ``body`` - ## has to be in a DSL that is a particular subset of the language. Please - ## refer to the manual for further information. + ## A parallel section can be used to execute a block in parallel. + ## + ## ``body`` has to be in a DSL that is a particular subset of the language. + ## + ## Please refer to `the manual <manual.html#parallel-amp-spawn>`_ + ## for further information. var state: ThreadPoolState @@ -547,8 +567,9 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} = proc sync*() = - ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate - ## waiting, you have to use an explicit barrier. + ## A simple barrier to wait for all ``spawn``'ed tasks. + ## + ## If you need more elaborate waiting, you have to use an explicit barrier. var toRelease = 0 while true: var allReady = true diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 6535bb0d3..a08a583c0 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -9,11 +9,11 @@ ## This module implements efficient computations of hash values for diverse ## Nim types. All the procs are based on these two building blocks: -## - `!& proc <#!&>`_ used to start or mix a hash value, and -## - `!$ proc <#!$>`_ used to *finish* the hash value. -## If you want to implement hash procs for -## your custom types you will end up writing the following kind of skeleton of -## code: +## - `!& proc <#!&,Hash,int>`_ used to start or mix a hash value, and +## - `!$ proc <#!$,Hash>`_ used to *finish* the hash value. +## +## If you want to implement hash procs for your custom types, +## you will end up writing the following kind of skeleton of code: ## ## .. code-block:: Nim ## proc hash(x: Something): Hash = @@ -37,31 +37,40 @@ ## h = h !& hash(x.foo) ## h = h !& hash(x.bar) ## result = !$h +## +## **See also:** +## * `md5 module <md5.html>`_ for MD5 checksum algorithm +## * `base64 module <base64.html>`_ for a base64 encoder and decoder +## * `std/sha1 module <sha1.html>`_ for a sha1 encoder and decoder +## * `tables modlule <tables.html>`_ for hash tables + import strutils type - Hash* = int ## a hash value; hash tables using these values should + Hash* = int ## A hash value. Hash tables using these values should ## always have a size of a power of two and can use the ``and`` ## operator instead of ``mod`` for truncation of the hash value. proc `!&`*(h: Hash, val: int): Hash {.inline.} = - ## mixes a hash value `h` with `val` to produce a new hash value. This is - ## only needed if you need to implement a hash proc for a new datatype. + ## Mixes a hash value `h` with `val` to produce a new hash value. + ## + ## This is only needed if you need to implement a hash proc for a new datatype. result = h +% val result = result +% result shl 10 result = result xor (result shr 6) proc `!$`*(h: Hash): Hash {.inline.} = - ## finishes the computation of the hash value. This is - ## only needed if you need to implement a hash proc for a new datatype. + ## Finishes the computation of the hash value. + ## + ## This is only needed if you need to implement a hash proc for a new datatype. result = h +% h shl 3 result = result xor (result shr 11) result = result +% result shl 15 proc hashData*(data: pointer, size: int): Hash = - ## hashes an array of bytes of size `size` + ## Hashes an array of bytes of size `size`. var h: Hash = 0 when defined(js): var p: cstring @@ -80,7 +89,7 @@ when defined(js): var objectID = 0 proc hash*(x: pointer): Hash {.inline.} = - ## efficient hashing of pointers + ## Efficient hashing of pointers. when defined(js): asm """ if (typeof `x` == "object") { @@ -97,45 +106,57 @@ proc hash*(x: pointer): Hash {.inline.} = when not defined(booting): proc hash*[T: proc](x: T): Hash {.inline.} = - ## efficient hashing of proc vars; closures are supported too. + ## Efficient hashing of proc vars. Closures are supported too. when T is "closure": result = hash(rawProc(x)) !& hash(rawEnv(x)) else: result = hash(pointer(x)) proc hash*(x: int): Hash {.inline.} = - ## efficient hashing of integers + ## Efficient hashing of integers. result = x proc hash*(x: int64): Hash {.inline.} = - ## efficient hashing of int64 integers + ## Efficient hashing of `int64` integers. result = toU32(x) proc hash*(x: uint): Hash {.inline.} = - ## efficient hashing of unsigned integers + ## Efficient hashing of unsigned integers. result = cast[int](x) proc hash*(x: uint64): Hash {.inline.} = - ## efficient hashing of uint64 integers + ## Efficient hashing of `uint64` integers. result = toU32(cast[int](x)) proc hash*(x: char): Hash {.inline.} = - ## efficient hashing of characters + ## Efficient hashing of characters. result = ord(x) proc hash*[T: Ordinal](x: T): Hash {.inline.} = - ## efficient hashing of other ordinal types (e.g., enums) + ## Efficient hashing of other ordinal types (e.g. enums). result = ord(x) proc hash*(x: string): Hash = - ## efficient hashing of strings + ## Efficient hashing of strings. + ## + ## See also: + ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_ + ## * `hashIgnoreCase <#hashIgnoreCase,string>`_ + runnableExamples: + doAssert hash("abracadabra") == -5600162842546114722 + doAssert hash("Abracadabra") == 2068684413884279454 + var h: Hash = 0 for i in 0..x.len-1: h = h !& ord(x[i]) result = !$h proc hash*(x: cstring): Hash = - ## efficient hashing of null-terminated strings + ## Efficient hashing of null-terminated strings. + runnableExamples: + doAssert hash(cstring"abracadabra") == -5600162842546114722 + doAssert hash(cstring"Abracadabra") == 2068684413884279454 + var h: Hash = 0 var i = 0 when defined(js): @@ -149,17 +170,27 @@ proc hash*(x: cstring): Hash = result = !$h proc hash*(sBuf: string, sPos, ePos: int): Hash = - ## efficient hashing of a string buffer, from starting - ## position `sPos` to ending position `ePos` + ## Efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos` (included). ## - ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)`` + ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``. + runnableExamples: + var a = "abracadabra" + doAssert hash(a, 0, 3) == hash(a, 7, 10) + var h: Hash = 0 for i in sPos..ePos: h = h !& ord(sBuf[i]) result = !$h proc hashIgnoreStyle*(x: string): Hash = - ## efficient hashing of strings; style is ignored + ## Efficient hashing of strings; style is ignored. + ## + ## See also: + ## * `hashIgnoreCase <#hashIgnoreCase,string>`_ + runnableExamples: + doAssert hashIgnoreStyle("aBr_aCa_dAB_ra") == hash("abracadabra") + var h: Hash = 0 var i = 0 let xLen = x.len @@ -176,11 +207,15 @@ proc hashIgnoreStyle*(x: string): Hash = result = !$h proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = - ## efficient hashing of a string buffer, from starting - ## position `sPos` to ending position `ePos`; style is ignored + ## Efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos` (included); style is ignored. ## ## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent - ## to ``hashIgnoreStyle(myBuf)`` + ## to ``hashIgnoreStyle(myBuf)``. + runnableExamples: + var a = "ABracada_b_r_a" + doAssert hashIgnoreStyle(a, 0, 3) == hashIgnoreStyle(a, 7, a.high) + var h: Hash = 0 var i = sPos while i <= ePos: @@ -195,7 +230,13 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = result = !$h proc hashIgnoreCase*(x: string): Hash = - ## efficient hashing of strings; case is ignored + ## Efficient hashing of strings; case is ignored. + ## + ## See also: + ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_ + runnableExamples: + doAssert hashIgnoreCase("ABRAcaDABRA") == hashIgnoreCase("abRACAdabra") + var h: Hash = 0 for i in 0..x.len-1: var c = x[i] @@ -205,11 +246,15 @@ proc hashIgnoreCase*(x: string): Hash = result = !$h proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = - ## efficient hashing of a string buffer, from starting - ## position `sPos` to ending position `ePos`; case is ignored + ## Efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos` (included); case is ignored. ## ## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent - ## to ``hashIgnoreCase(myBuf)`` + ## to ``hashIgnoreCase(myBuf)``. + runnableExamples: + var a = "ABracadabRA" + doAssert hashIgnoreCase(a, 0, 3) == hashIgnoreCase(a, 7, 10) + var h: Hash = 0 for i in sPos..ePos: var c = sBuf[i] @@ -219,7 +264,7 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = result = !$h proc hash*(x: float): Hash {.inline.} = - ## efficient hashing of floats. + ## Efficient hashing of floats. var y = x + 1.0 result = cast[ptr Hash](addr(y))[] @@ -231,34 +276,40 @@ proc hash*[A](x: set[A]): Hash proc hash*[T: tuple](x: T): Hash = - ## efficient hashing of tuples. + ## Efficient hashing of tuples. for f in fields(x): result = result !& hash(f) result = !$result proc hash*[A](x: openArray[A]): Hash = - ## efficient hashing of arrays and sequences. + ## Efficient hashing of arrays and sequences. for it in items(x): result = result !& hash(it) result = !$result proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = - ## efficient hashing of portions of arrays and sequences. + ## Efficient hashing of portions of arrays and sequences, from starting + ## position `sPos` to ending position `ePos` (included). ## - ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)`` + ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``. + runnableExamples: + let a = [1, 2, 5, 1, 2, 6] + doAssert hash(a, 0, 1) == hash(a, 3, 4) + for i in sPos..ePos: result = result !& hash(aBuf[i]) result = !$result proc hash*[A](x: set[A]): Hash = - ## efficient hashing of sets. + ## Efficient hashing of sets. for it in items(x): result = result !& hash(it) result = !$result + 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") ) + doAssert( hashIgnoreCase("aA bb aAAa1234") == hash("aa bb aaaa1234") ) + doAssert( hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234") ) let xx = @['H','e','l','l','o'] let ss = "Hello" doAssert( hash(xx) == hash(ss) ) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 1ff3a9824..028223c93 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -7,13 +7,20 @@ # distribution, for details about the copyright. # -## Module for computing MD5 checksums. +## Module for computing `MD5 checksums <https://en.wikipedia.org/wiki/MD5>`_. +## +## **See also:** +## * `base64 module<base64.html>`_ implements a base64 encoder and decoder +## * `std/sha1 module <sha1.html>`_ for a sha1 encoder and decoder +## * `hashes module<hashes.html>`_ for efficient computations of hash values +## for diverse Nim types type MD5State = array[0..3, uint32] MD5Block = array[0..15, uint32] MD5CBits = array[0..7, uint8] - MD5Digest* = array[0..15, uint8] + MD5Digest* = array[0..15, uint8] ## \ + ## MD5 checksum of a string, obtained with `toMD5 proc <#toMD5,string>`_. MD5Buffer = array[0..63, uint8] MD5Context* {.final.} = object state: MD5State @@ -161,8 +168,62 @@ proc transform(buffer: pointer, state: var MD5State) = state[2] = state[2] + c state[3] = state[3] + d +proc md5Init*(c: var MD5Context) {.raises: [], tags: [].} +proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], tags: [].} +proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [].} + + +proc toMD5*(s: string): MD5Digest = + ## Computes the `MD5Digest` value for a string `s`. + ## + ## See also: + ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation + ## of the `MD5Digest` + ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string + runnableExamples: + assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" + + var c: MD5Context + md5Init(c) + md5Update(c, cstring(s), len(s)) + md5Final(c, result) + +proc `$`*(d: MD5Digest): string = + ## Converts a `MD5Digest` value into its string representation. + const digits = "0123456789abcdef" + result = "" + for i in 0..15: + add(result, digits[(d[i].int shr 4) and 0xF]) + add(result, digits[d[i].int and 0xF]) + +proc getMD5*(s: string): string = + ## Computes an MD5 value of `s` and returns its string representation. + ## + ## See also: + ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string + runnableExamples: + assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" + + var + c: MD5Context + d: MD5Digest + md5Init(c) + md5Update(c, cstring(s), len(s)) + md5Final(c, d) + result = $d + +proc `==`*(D1, D2: MD5Digest): bool = + ## Checks if two `MD5Digest` values are identical. + for i in 0..15: + if D1[i] != D2[i]: return false + return true + + proc md5Init*(c: var MD5Context) = - ## initializes a MD5Context + ## Initializes a `MD5Context`. + ## + ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this + ## function explicitly. c.state[0] = 0x67452301'u32 c.state[1] = 0xEFCDAB89'u32 c.state[2] = 0x98BADCFE'u32 @@ -172,7 +233,10 @@ proc md5Init*(c: var MD5Context) = zeroMem(addr(c.buffer), sizeof(MD5buffer)) proc md5Update*(c: var MD5Context, input: cstring, len: int) = - ## updates the MD5Context with the `input` data of length `len` + ## Updates the `MD5Context` with the `input` data of length `len`. + ## + ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this + ## function explicitly. var input = input var Index = int((c.count[0] shr 3) and 0x3F) c.count[0] = c.count[0] + (uint32(len) shl 3) @@ -191,7 +255,10 @@ proc md5Update*(c: var MD5Context, input: cstring, len: int) = copyMem(addr(c.buffer[Index]), addr(input[0]), len) proc md5Final*(c: var MD5Context, digest: var MD5Digest) = - ## finishes the MD5Context and stores the result in `digest` + ## Finishes the `MD5Context` and stores the result in `digest`. + ## + ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this + ## function explicitly. var Bits: MD5CBits PadLen: int @@ -204,36 +271,6 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) = decode(digest, c.state) zeroMem(addr(c), sizeof(MD5Context)) -proc toMD5*(s: string): MD5Digest = - ## computes the MD5Digest value for a string `s` - var c: MD5Context - md5Init(c) - md5Update(c, cstring(s), len(s)) - md5Final(c, result) - -proc `$`*(d: MD5Digest): string = - ## converts a MD5Digest value into its string representation - const digits = "0123456789abcdef" - result = "" - for i in 0..15: - add(result, digits[(d[i].int shr 4) and 0xF]) - add(result, digits[d[i].int and 0xF]) - -proc getMD5*(s: string): string = - ## computes an MD5 value of `s` and returns its string representation - var - c: MD5Context - d: MD5Digest - md5Init(c) - md5Update(c, cstring(s), len(s)) - md5Final(c, d) - result = $d - -proc `==`*(D1, D2: MD5Digest): bool = - ## checks if two MD5Digest values are identical - for i in 0..15: - if D1[i] != D2[i]: return false - return true when isMainModule: assert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index a373a9370..b6cbaf558 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -8,7 +8,7 @@ # ## This module defines compile-time reflection procs for -## working with types +## working with types. include "system/helpers" # for `isNamedTuple` @@ -16,11 +16,13 @@ export system.`$` export isNamedTuple proc name*(t: typedesc): string {.magic: "TypeTrait".} - ## Alias for system.`$`(t) since Nim v0.20.0. + ## Returns the name of the given type. + ## + ## Alias for system.`$`(t) since Nim v0.20. proc arity*(t: typedesc): int {.magic: "TypeTrait".} = - ## Returns the arity of the given type. This is the number of "type" components or - ## the number of generic parameters a given type ``t`` has. + ## Returns the arity of the given type. This is the number of "type" + ## components or the number of generic parameters a given type ``t`` has. runnableExamples: assert arity(seq[string]) == 1 assert arity(array[3, int]) == 2 @@ -31,19 +33,35 @@ proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".} ## uninstantiated form. ## ## For example: - ## seq[int].genericHead will be just seq - ## seq[int].genericHead[float] will be seq[float] + ## * `seq[int].genericHead` will be just `seq` + ## * `seq[int].genericHead[float]` will be `seq[float]` ## ## A compile-time error will be produced if the supplied type ## is not generic. + ## + ## See also: + ## * `stripGenericParams <#stripGenericParams,typedesc>`_ + ## + ## Example: + ## + ## .. code-block:: nim + ## type + ## Functor[A] = concept f + ## type MatchedGenericType = genericHead(f.type) + ## # `f` will be a value of a type such as `Option[T]` + ## # `MatchedGenericType` will become the `Option` type + proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} - ## This trait is similar to `genericHead`, but instead of producing - ## error for non-generic types, it will just return them unmodified. + ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but + ## instead of producing error for non-generic types, it will just return + ## them unmodified. proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".} ## This trait returns true iff the type ``t`` is safe to use for - ## `copyMem`:idx:. Other languages name a type like these `blob`:idx:. + ## `copyMem`:idx:. + ## + ## Other languages name a type like these `blob`:idx:. when isMainModule: diff --git a/lib/system/channels.nim b/lib/system/channels.nim index e0eb9ae13..27293c2d4 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -178,7 +178,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, copyMem(dest, src, mt.size) # copy raw bits proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) = - ## adds an `item` to the end of the queue `q`. + ## Adds an `item` to the end of the queue `q`. var cap = q.mask+1 if q.count >= cap: # start with capacity for 2 entries in the queue: @@ -232,11 +232,14 @@ proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool result = true proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} = - ## sends a message to a thread. `msg` is deeply copied. + ## Sends a message to a thread. `msg` is deeply copied. discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false) proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} = - ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block. + ## Tries to send a message to a thread. + ## + ## `msg` is deeply copied. Doesn't block. + ## ## Returns `false` if the message was not sent because number of pending items ## in the channel exceeded `maxItems`. sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true) @@ -255,8 +258,10 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) = signalSysCond(q.cond) proc recv*[TMsg](c: var Channel[TMsg]): TMsg = - ## receives a message from the channel `c`. This blocks until - ## a message has arrived! You may use ``peek`` to avoid the blocking. + ## Receives a message from the channel `c`. + ## + ## This blocks until a message has arrived! + ## You may use `peek proc <#peek,Channel[TMsg]>`_ to avoid the blocking. var q = cast[PRawChannel](addr(c)) acquireSys(q.lock) llRecv(q, addr(result), cast[PNimType](getTypeInfo(result))) @@ -265,8 +270,9 @@ proc recv*[TMsg](c: var Channel[TMsg]): TMsg = proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool, msg: TMsg] = ## Tries to receive a message from the channel `c`, but this can fail - ## for all sort of reasons, including contention. If it fails, - ## it returns ``(false, default(msg))`` otherwise it + ## for all sort of reasons, including contention. + ## + ## If it fails, it returns ``(false, default(msg))`` otherwise it ## returns ``(true, msg)``. var q = cast[PRawChannel](addr(c)) if q.mask != ChannelDeadMask: @@ -277,9 +283,12 @@ proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool, releaseSys(q.lock) proc peek*[TMsg](c: var Channel[TMsg]): int = - ## returns the current number of messages in the channel `c`. Returns -1 - ## if the channel has been closed. **Note**: This is dangerous to use - ## as it encourages races. It's much better to use ``tryRecv`` instead. + ## Returns the current number of messages in the channel `c`. + ## + ## Returns -1 if the channel has been closed. + ## + ## **Note**: This is dangerous to use as it encourages races. + ## It's much better to use `tryRecv proc <#tryRecv,Channel[TMsg]>`_ instead. var q = cast[PRawChannel](addr(c)) if q.mask != ChannelDeadMask: lockChannel(q): @@ -288,17 +297,20 @@ proc peek*[TMsg](c: var Channel[TMsg]): int = result = -1 proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) = - ## opens a channel `c` for inter thread communication. The `send` operation - ## will block until number of unprocessed items is less than `maxItems`. + ## Opens a channel `c` for inter thread communication. + ## + ## The `send` operation will block until number of unprocessed items is + ## less than `maxItems`. + ## ## For unlimited queue set `maxItems` to 0. initRawChannel(addr(c), maxItems) proc close*[TMsg](c: var Channel[TMsg]) = - ## closes a channel `c` and frees its associated resources. + ## Closes a channel `c` and frees its associated resources. deinitRawChannel(addr(c)) proc ready*[TMsg](c: var Channel[TMsg]): bool = - ## returns true iff some thread is waiting on the channel `c` for + ## Returns true iff some thread is waiting on the channel `c` for ## new messages. var q = cast[PRawChannel](addr(c)) result = q.ready diff --git a/lib/system/threads.nim b/lib/system/threads.nim index bbe170376..7a988db46 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -7,8 +7,10 @@ # distribution, for details about the copyright. # -## Thread support for Nim. **Note**: This is part of the system module. -## Do not import it directly. To activate thread support you need to compile +## Thread support for Nim. +## +## **Note**: This is part of the system module. Do not import it directly. +## To activate thread support you need to compile ## with the ``--threads:on`` command line switch. ## ## Nim's memory model for threads is quite different from other common @@ -381,6 +383,7 @@ var proc onThreadDestruction*(handler: proc () {.closure, gcsafe.}) = ## Registers a *thread local* handler that is called at the thread's ## destruction. + ## ## A thread is destructed when the ``.thread`` proc returns ## normally or when it raises an exception. Note that unhandled exceptions ## in a thread nevertheless cause the whole process to die. @@ -481,22 +484,22 @@ else: {.pop.} proc running*[TArg](t: Thread[TArg]): bool {.inline.} = - ## returns true if `t` is running. + ## Returns true if `t` is running. result = t.dataFn != nil proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} = - ## returns the thread handle of `t`. + ## Returns the thread handle of `t`. result = t.sys when hostOS == "windows": const MAXIMUM_WAIT_OBJECTS = 64 proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = - ## waits for the thread `t` to finish. + ## Waits for the thread `t` to finish. discard waitForSingleObject(t.sys, -1'i32) proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = - ## waits for every thread in `t` to finish. + ## Waits for every thread in `t` to finish. var a: array[MAXIMUM_WAIT_OBJECTS, SysThread] var k = 0 while k < len(t): @@ -508,25 +511,25 @@ when hostOS == "windows": elif defined(genode): proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.} - ## waits for the thread `t` to finish. + ## Waits for the thread `t` to finish. proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = - ## waits for every thread in `t` to finish. + ## Waits for every thread in `t` to finish. for i in 0..t.high: joinThread(t[i]) else: proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = - ## waits for the thread `t` to finish. + ## Waits for the thread `t` to finish. discard pthread_join(t.sys, nil) proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = - ## waits for every thread in `t` to finish. + ## Waits for every thread in `t` to finish. for i in 0..t.high: joinThread(t[i]) when false: # XXX a thread should really release its heap here somehow: proc destroyThread*[TArg](t: var Thread[TArg]) = - ## forces the thread `t` to terminate. This is potentially dangerous if + ## Forces the thread `t` to terminate. This is potentially dangerous if ## you don't have full control over `t` and its acquired resources. when hostOS == "windows": discard TerminateThread(t.sys, 1'i32) @@ -543,8 +546,10 @@ when hostOS == "windows": proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, param: TArg) = - ## creates a new thread `t` and starts its execution. Entry point is the - ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you + ## Creates a new thread `t` and starts its execution. + ## + ## Entry point is the proc `tp`. + ## `param` is passed to `tp`. `TArg` can be ``void`` if you ## don't need to pass any data to the thread. t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) @@ -558,14 +563,15 @@ when hostOS == "windows": raise newException(ResourceExhaustedError, "cannot create thread") proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = - ## pins a thread to a `CPU`:idx:. In other words sets a - ## thread's `affinity`:idx:. If you don't know what this means, you - ## shouldn't use this proc. + ## Pins a thread to a `CPU`:idx:. + ## + ## In other words sets a thread's `affinity`:idx:. + ## If you don't know what this means, you shouldn't use this proc. setThreadAffinityMask(t.sys, uint(1 shl cpu)) elif defined(genode): var affinityOffset: cuint = 1 - ## CPU affinity offset for next thread, safe to roll-over + ## CPU affinity offset for next thread, safe to roll-over. proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, @@ -589,8 +595,10 @@ else: proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, param: TArg) = - ## creates a new thread `t` and starts its execution. Entry point is the - ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you + ## Creates a new thread `t` and starts its execution. + ## + ## Entry point is the proc `tp`. `param` is passed to `tp`. + ## `TArg` can be ``void`` if you ## don't need to pass any data to the thread. t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) @@ -604,9 +612,10 @@ else: raise newException(ResourceExhaustedError, "cannot create thread") proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = - ## pins a thread to a `CPU`:idx:. In other words sets a - ## thread's `affinity`:idx:. If you don't know what this means, you - ## shouldn't use this proc. + ## Pins a thread to a `CPU`:idx:. + ## + ## In other words sets a thread's `affinity`:idx:. + ## If you don't know what this means, you shouldn't use this proc. when not defined(macosx): var s {.noinit.}: CpuSet cpusetZero(s) @@ -618,7 +627,7 @@ proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) = when false: proc mainThreadId*[TArg](): ThreadId[TArg] = - ## returns the thread ID of the main thread. + ## Returns the thread ID of the main thread. result = cast[ThreadId[TArg]](addr(mainThread)) when useStackMaskHack: @@ -632,7 +641,7 @@ var threadId {.threadvar.}: int when defined(windows): proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(getCurrentThreadId()) result = threadId @@ -645,7 +654,7 @@ elif defined(linux): var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: clong proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(syscall(NR_gettid)) result = threadId @@ -654,7 +663,7 @@ elif defined(dragonfly): proc lwp_gettid(): int32 {.importc, header: "unistd.h".} proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(lwp_gettid()) result = threadId @@ -672,7 +681,7 @@ elif defined(netbsd): proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".} proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(lwp_self()) result = threadId @@ -682,7 +691,7 @@ elif defined(freebsd): var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. var tid = 0.cint if threadId == 0: discard syscall(SYS_thr_self, addr tid) @@ -694,7 +703,7 @@ elif defined(macosx): var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(syscall(SYS_thread_selfid)) result = threadId @@ -704,7 +713,7 @@ elif defined(solaris): proc thr_self(): thread_t {.importc, header: "<thread.h>".} proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(thr_self()) result = threadId @@ -714,7 +723,7 @@ elif defined(haiku): proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".} proc getThreadId*(): int = - ## get the ID of the currently running thread. + ## Gets the ID of the currently running thread. if threadId == 0: threadId = int(find_thread(nil)) result = threadId |