diff options
author | EXetoC <exetoc@gmail.com> | 2014-03-06 02:46:31 +0100 |
---|---|---|
committer | EXetoC <exetoc@gmail.com> | 2014-03-06 02:46:31 +0100 |
commit | d1bc6cf0981eddf283515c2d77bccca76abff99f (patch) | |
tree | ac28e32bd6143c7a06c44dbea632f28f65ea3d20 /lib | |
parent | 853bdbf4948db7774bc3caf74f67ba1228f65016 (diff) | |
parent | 7904446c47f952a026d408f04431dbaf6d887b37 (diff) | |
download | Nim-d1bc6cf0981eddf283515c2d77bccca76abff99f.tar.gz |
Merge branch 'devel' into alloc-overloads
Diffstat (limited to 'lib')
-rw-r--r-- | lib/core/macros.nim | 2 | ||||
-rw-r--r-- | lib/impure/db_mysql.nim | 2 | ||||
-rw-r--r-- | lib/nimbase.h | 4 | ||||
-rw-r--r-- | lib/packages/docutils/docutils.babel | 6 | ||||
-rw-r--r-- | lib/packages/docutils/highlite.nim | 7 | ||||
-rw-r--r-- | lib/packages/docutils/rst.nim | 2 | ||||
-rw-r--r-- | lib/posix/epoll.nim | 90 | ||||
-rw-r--r-- | lib/posix/linux.nim | 25 | ||||
-rw-r--r-- | lib/posix/posix.nim | 4 | ||||
-rw-r--r-- | lib/pure/asyncio.nim | 10 | ||||
-rw-r--r-- | lib/pure/asyncio2.nim | 922 | ||||
-rw-r--r-- | lib/pure/collections/sets.nim | 4 | ||||
-rw-r--r-- | lib/pure/dynlib.nim | 10 | ||||
-rw-r--r-- | lib/pure/htmlgen.nim | 11 | ||||
-rw-r--r-- | lib/pure/net.nim | 55 | ||||
-rw-r--r-- | lib/pure/nimprof.nim | 4 | ||||
-rw-r--r-- | lib/pure/os.nim | 8 | ||||
-rw-r--r-- | lib/pure/osproc.nim | 281 | ||||
-rw-r--r-- | lib/pure/pegs.nim | 8 | ||||
-rw-r--r-- | lib/pure/selectors.nim | 250 | ||||
-rw-r--r-- | lib/pure/sockets2.nim | 213 | ||||
-rw-r--r-- | lib/pure/times.nim | 250 | ||||
-rw-r--r-- | lib/stdlib.babel | 6 | ||||
-rw-r--r-- | lib/system.nim | 15 | ||||
-rw-r--r-- | lib/system/arithm.nim | 12 | ||||
-rw-r--r-- | lib/system/gc.nim | 2 | ||||
-rw-r--r-- | lib/system/jssys.nim | 117 | ||||
-rw-r--r-- | lib/windows/winlean.nim | 83 | ||||
-rw-r--r-- | lib/wrappers/zip/zlib.nim | 1 |
29 files changed, 2104 insertions, 300 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 585ccf869..d14822974 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -516,7 +516,7 @@ proc last*(node: PNimrodNode): PNimrodNode {.compileTime.} = node[node.high] const - RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda} + RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef} AtomicNodes* = {nnkNone..nnkNilLit} CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit, nnkHiddenCallConv} diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 8cdccda01..74aaa1a59 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -87,7 +87,7 @@ proc newRow(L: int): TRow = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = if row != nil: - while mysql.FetchRow(sqlres) != nil: nil + while mysql.FetchRow(sqlres) != nil: discard mysql.FreeResult(sqlres) iterator fastRows*(db: TDbConn, query: TSqlQuery, diff --git a/lib/nimbase.h b/lib/nimbase.h index 19d161adf..f73dca190 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) { typedef struct TStringDesc* string; -/* declared size of a sequence: */ -#if defined(__GNUC__) +/* declared size of a sequence/variable length array: */ +#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) # define SEQ_DECL_SIZE /* empty is correct! */ #else # define SEQ_DECL_SIZE 1000000 diff --git a/lib/packages/docutils/docutils.babel b/lib/packages/docutils/docutils.babel new file mode 100644 index 000000000..1ed86ca05 --- /dev/null +++ b/lib/packages/docutils/docutils.babel @@ -0,0 +1,6 @@ +[Package] +name = "docutils" +version = "0.9.0" +author = "Andreas Rumpf" +description = "Nimrod's reStructuredText processor." +license = "MIT" diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 4ca0c79e0..c507f5e1c 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -61,9 +61,8 @@ proc getSourceLanguage*(name: string): TSourceLanguage = if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: return i result = langNone - -proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = - g.buf = cstring(buf) +proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) = + g.buf = buf g.kind = low(TTokenClass) g.start = 0 g.length = 0 @@ -71,6 +70,8 @@ proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = var pos = 0 # skip initial whitespace: while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) g.pos = pos +proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = + initGeneralTokenizer(g, cstring(buf)) proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) = discard diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index bb018bc1e..30cc9026b 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1543,7 +1543,7 @@ proc dirRaw(p: var TRstParser): PRstNode = elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0: dirRawAux(p, result, rnRawLatex, parseLiteralBlock) else: - rstMessage(p, meInvalidDirective, result.sons[0].text) + rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text) else: dirRawAux(p, result, rnRaw, parseSectionWrapper) diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim new file mode 100644 index 000000000..366521551 --- /dev/null +++ b/lib/posix/epoll.nim @@ -0,0 +1,90 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2013 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +from posix import TSocketHandle + +const + EPOLLIN* = 0x00000001 + EPOLLPRI* = 0x00000002 + EPOLLOUT* = 0x00000004 + EPOLLERR* = 0x00000008 + EPOLLHUP* = 0x00000010 + EPOLLRDNORM* = 0x00000040 + EPOLLRDBAND* = 0x00000080 + EPOLLWRNORM* = 0x00000100 + EPOLLWRBAND* = 0x00000200 + EPOLLMSG* = 0x00000400 + EPOLLRDHUP* = 0x00002000 + EPOLLWAKEUP* = 1 shl 29 + EPOLLONESHOT* = 1 shl 30 + EPOLLET* = 1 shl 31 + +# Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). + +const + EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface. + EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface. + EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure. + +type + epoll_data* {.importc: "union epoll_data", + header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union. + #thePtr* {.importc: "ptr".}: pointer + fd*: cint # \ + #u32*: uint32 + #u64*: uint64 + + epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object + events*: uint32 # Epoll events + data*: epoll_data # User data variable + +proc epoll_create*(size: cint): cint {.importc: "epoll_create", + header: "<sys/epoll.h>".} + ## Creates an epoll instance. Returns an fd for the new instance. + ## The "size" parameter is a hint specifying the number of file + ## descriptors to be associated with the new instance. The fd + ## returned by epoll_create() should be closed with close(). + +proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", + header: "<sys/epoll.h>".} + ## Same as epoll_create but with an FLAGS parameter. The unused SIZE + ## parameter has been dropped. + +proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {. + importc: "epoll_ctl", header: "<sys/epoll.h>".} + ## Manipulate an epoll instance "epfd". Returns 0 in case of success, + ## -1 in case of error ( the "errno" variable will contain the + ## specific error code ) The "op" parameter is one of the EPOLL_CTL_* + ## constants defined above. The "fd" parameter is the target of the + ## operation. The "event" parameter describes which events the caller + ## is interested in and any associated user data. + +proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint; + timeout: cint): cint {.importc: "epoll_wait", + header: "<sys/epoll.h>".} + ## Wait for events on an epoll instance "epfd". Returns the number of + ## triggered events returned in "events" buffer. Or -1 in case of + ## error with the "errno" variable set to the specific error code. The + ## "events" parameter is a buffer that will contain triggered + ## events. The "maxevents" is the maximum number of events to be + ## returned ( usually size of "events" ). The "timeout" parameter + ## specifies the maximum wait time in milliseconds (-1 == infinite). + ## + ## This function is a cancellation point and therefore not marked with + ## __THROW. + + +#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint; +# timeout: cint; ss: ptr sigset_t): cint {. +# importc: "epoll_pwait", header: "<sys/epoll.h>".} +# Same as epoll_wait, but the thread's signal mask is temporarily +# and atomically replaced with the one provided as parameter. +# +# This function is a cancellation point and therefore not marked with +# __THROW. diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim new file mode 100644 index 000000000..1ed1af3b6 --- /dev/null +++ b/lib/posix/linux.nim @@ -0,0 +1,25 @@ +import posix + +const + CSIGNAL* = 0x000000FF + CLONE_VM* = 0x00000100 + CLONE_FS* = 0x00000200 + CLONE_FILES* = 0x00000400 + CLONE_SIGHAND* = 0x00000800 + CLONE_PTRACE* = 0x00002000 + CLONE_VFORK* = 0x00004000 + CLONE_PARENT* = 0x00008000 + CLONE_THREAD* = 0x00010000 + CLONE_NEWNS* = 0x00020000 + CLONE_SYSVSEM* = 0x00040000 + CLONE_SETTLS* = 0x00080000 + CLONE_PARENT_SETTID* = 0x00100000 + CLONE_CHILD_CLEARTID* = 0x00200000 + CLONE_DETACHED* = 0x00400000 + CLONE_UNTRACED* = 0x00800000 + CLONE_CHILD_SETTID* = 0x01000000 + CLONE_STOPPED* = 0x02000000 + +# fn should be of type proc (a2: pointer): void {.cdecl.} +proc clone*(fn: pointer; child_stack: pointer; flags: cint; + arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "<sched.h>".} diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 41260b36f..131f23fdd 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -2066,6 +2066,7 @@ proc pthread_spin_unlock*(a1: ptr Tpthread_spinlock): cint {. proc pthread_testcancel*() {.importc, header: "<pthread.h>".} +proc exitnow*(code: int): void {.importc: "_exit", header: "<unistd.h>".} proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".} proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".} proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".} @@ -2265,6 +2266,7 @@ proc gmtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h> proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "<time.h>".} proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>".} proc mktime*(a1: var Ttm): TTime {.importc, header: "<time.h>".} +proc timegm*(a1: var Ttm): TTime {.importc, header: "<time.h>".} proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "<time.h>".} proc strftime*(a1: cstring, a2: int, a3: cstring, a4: var Ttm): int {.importc, header: "<time.h>".} @@ -2356,7 +2358,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "<sys/select.h>".} proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec, a6: var Tsigset): cint {.importc, header: "<sys/select.h>".} -proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. +proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. importc, header: "<sys/select.h>".} when hasSpawnH: diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 96afc6f4f..ab09dc860 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -167,7 +167,7 @@ proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, result = newAsyncSocket() result.socket = socket(domain, typ, protocol, buffered) result.proto = protocol - if result.socket == InvalidSocket: OSError(OSLastError()) + if result.socket == invalidSocket: osError(osLastError()) result.socket.setBlocking(false) proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = @@ -357,7 +357,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, client.sslNeedAccept = false client.info = SockConnected - if c == InvalidSocket: SocketError(server.socket) + if c == invalidSocket: socketError(server.socket) c.setBlocking(false) # TODO: Needs to be tested. # deleg.open is set in ``toDelegate``. @@ -481,7 +481,7 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} = of RecvDisconnected: result = true of RecvFail: - s.SocketError(async = true) + s.socketError(async = true) result = false {.pop.} @@ -615,11 +615,11 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool = if d.hasDataBuffered(d.deleVal): hasDataBufferedCount.inc() d.handleRead(d.deleVal) - if hasDataBufferedCount > 0: return True + if hasDataBufferedCount > 0: return true if readDg.len() == 0 and writeDg.len() == 0: ## TODO: Perhaps this shouldn't return if errorDg has something? - return False + return false if select(readDg, writeDg, errorDg, timeout) != 0: for i in 0..len(d.delegates)-1: diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim new file mode 100644 index 000000000..12d4cb5a3 --- /dev/null +++ b/lib/pure/asyncio2.nim @@ -0,0 +1,922 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import os, oids, tables, strutils, macros + +import sockets2, net + +## Asyncio2 +## -------- +## +## This module implements a brand new asyncio module based on Futures. +## IOCP is used under the hood on Windows and the selectors module is used for +## other operating systems. + +# -- Futures + +type + PFutureBase* = ref object of PObject + cb: proc () {.closure.} + finished: bool + + PFuture*[T] = ref object of PFutureBase + value: T + error: ref EBase + +proc newFuture*[T](): PFuture[T] = + ## Creates a new future. + new(result) + result.finished = false + +proc complete*[T](future: PFuture[T], val: T) = + ## Completes ``future`` with value ``val``. + assert(not future.finished, "Future already finished, cannot finish twice.") + assert(future.error == nil) + future.value = val + future.finished = true + if future.cb != nil: + future.cb() + +proc fail*[T](future: PFuture[T], error: ref EBase) = + ## Completes ``future`` with ``error``. + assert(not future.finished, "Future already finished, cannot finish twice.") + future.finished = true + future.error = error + if future.cb != nil: + future.cb() + +proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) = + ## Sets the callback proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + ## + ## **Note**: You most likely want the other ``callback`` setter which + ## passes ``future`` as a param to the callback. + future.cb = cb + if future.finished: + future.cb() + +proc `callback=`*[T](future: PFuture[T], + cb: proc (future: PFuture[T]) {.closure.}) = + ## Sets the callback proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + future.callback = proc () = cb(future) + +proc read*[T](future: PFuture[T]): T = + ## Retrieves the value of ``future``. Future must be finished otherwise + ## this function will fail with a ``EInvalidValue`` exception. + ## + ## If the result of the future is an error then that error will be raised. + if future.finished: + if future.error != nil: raise future.error + return future.value + else: + # TODO: Make a custom exception type for this? + raise newException(EInvalidValue, "Future still in progress.") + +proc finished*[T](future: PFuture[T]): bool = + ## Determines whether ``future`` has completed. + ## + ## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish. + future.finished + +proc failed*[T](future: PFuture[T]): bool = + ## Determines whether ``future`` completed with an error. + future.error != nil + +# TODO: Get rid of register. Do it implicitly. + +when defined(windows) or defined(nimdoc): + import winlean + type + TCompletionKey = dword + + TCompletionData* = object + sock: TSocketHandle + cb: proc (sock: TSocketHandle, bytesTransferred: DWORD, + errcode: TOSErrorCode) {.closure.} + + PDispatcher* = ref object + ioPort: THandle + hasHandles: bool + + TCustomOverlapped = object + Internal*: DWORD + InternalHigh*: DWORD + Offset*: DWORD + OffsetHigh*: DWORD + hEvent*: THANDLE + data*: TCompletionData + + PCustomOverlapped = ptr TCustomOverlapped + + proc newDispatcher*(): PDispatcher = + ## Creates a new Dispatcher instance. + new result + result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) + + proc register*(p: PDispatcher, sock: TSocketHandle) = + ## Registers ``sock`` with the dispatcher ``p``. + if CreateIOCompletionPort(sock.THandle, p.ioPort, + cast[TCompletionKey](sock), 1) == 0: + OSError(OSLastError()) + p.hasHandles = true + + proc poll*(p: PDispatcher, timeout = 500) = + ## Waits for completion events and processes them. + if not p.hasHandles: + raise newException(EInvalidValue, "No handles registered in dispatcher.") + + let llTimeout = + if timeout == -1: winlean.INFINITE + else: timeout.int32 + var lpNumberOfBytesTransferred: DWORD + var lpCompletionKey: ULONG + var lpOverlapped: POverlapped + let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred, + addr lpCompletionKey, addr lpOverlapped, llTimeout).bool + + # http://stackoverflow.com/a/12277264/492186 + # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html + var customOverlapped = cast[PCustomOverlapped](lpOverlapped) + if res: + # This is useful for ensuring the reliability of the overlapped struct. + assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, TOSErrorCode(-1)) + dealloc(customOverlapped) + else: + let errCode = OSLastError() + if lpOverlapped != nil: + assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, errCode) + dealloc(customOverlapped) + else: + if errCode.int32 == WAIT_TIMEOUT: + # Timed out + discard + else: OSError(errCode) + + var connectExPtr: pointer = nil + var acceptExPtr: pointer = nil + var getAcceptExSockAddrsPtr: pointer = nil + + proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool = + # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c + var bytesRet: DWord + func = nil + result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid, + sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD, + addr bytesRet, nil, nil) == 0 + + proc initAll() = + let dummySock = socket() + if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX): + OSError(OSLastError()) + if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX): + OSError(OSLastError()) + if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): + OSError(OSLastError()) + + proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint, + lpSendBuffer: pointer, dwSendDataLength: dword, + lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool = + if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().") + let func = + cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, + lpSendBuffer: pointer, dwSendDataLength: dword, + lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr) + + result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, + lpOverlapped) + + proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, + lpOverlapped: POverlapped): bool = + if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().") + let func = + cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, + lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr) + result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength, + dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, + lpOverlapped) + + proc getAcceptExSockaddrs(lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD, + LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint, + RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) = + if getAcceptExSockAddrsPtr.isNil: + raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().") + + let func = + cast[proc (lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr, + LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr, + RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr) + + func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, + RemoteSockaddr, RemoteSockaddrLength) + + proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, + af = AF_INET): PFuture[int] = + ## Connects ``socket`` to server at ``address:port``. + ## + ## Returns a ``PFuture`` which will complete when the connection succeeds + ## or an error occurs. + + var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed. + # Apparently ``ConnectEx`` expects the socket to be initially bound: + var saddr: Tsockaddr_in + saddr.sin_family = int16(toInt(af)) + saddr.sin_port = 0 + saddr.sin_addr.s_addr = INADDR_ANY + if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)), + sizeof(saddr).TSockLen) < 0'i32: + OSError(OSLastError()) + + var aiList = getAddrInfo(address, port, af) + var success = false + var lastError: TOSErrorCode + var it = aiList + while it != nil: + # "the OVERLAPPED structure must remain valid until the I/O completes" + # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx + var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) + ol.data = TCompletionData(sock: socket, cb: + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) + ) + + var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, + nil, 0, nil, cast[POverlapped](ol)) + if ret: + # Request to connect completed immediately. + success = true + retFuture.complete(0) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. + break + else: + lastError = OSLastError() + if lastError.int32 == ERROR_IO_PENDING: + # In this case ``ol`` will be deallocated in ``poll``. + success = true + break + else: + dealloc(ol) + success = false + it = it.ai_next + + dealloc(aiList) + if not success: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + return retFuture + + proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + flags: int = 0): PFuture[string] = + ## Reads ``size`` bytes from ``socket``. Returned future will complete once + ## all of the requested data is read. If socket is disconnected during the + ## recv operation then the future may complete with only a part of the + ## requested data read. If socket is disconnected and no data is available + ## to be read then the future will complete with a value of ``""``. + + var retFuture = newFuture[string]() + + var dataBuf: TWSABuf + dataBuf.buf = newString(size) + dataBuf.len = size + + var bytesReceived: DWord + var flagsio = flags.dword + var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) + ol.data = TCompletionData(sock: socket, cb: + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + if bytesCount == 0 and dataBuf.buf[0] == '\0': + retFuture.complete("") + else: + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) + ) + + let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, + addr flagsio, cast[POverlapped](ol), nil) + if ret == -1: + let err = OSLastError() + if err.int32 != ERROR_IO_PENDING: + retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc(ol) + elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': + # We have to ensure that the buffer is empty because WSARecv will tell + # us immediatelly when it was disconnected, even when there is still + # data in the buffer. + # We want to give the user as much data as we can. So we only return + # the empty string (which signals a disconnection) when there is + # nothing left to read. + retFuture.complete("") + # TODO: "For message-oriented sockets, where a zero byte message is often + # allowable, a failure with an error code of WSAEDISCON is used to + # indicate graceful closure." + # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx + else: + # Request to read completed immediately. + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. + return retFuture + + proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = + ## Sends ``data`` to ``socket``. The returned future will complete once all + ## data has been sent. + var retFuture = newFuture[int]() + + var dataBuf: TWSABuf + dataBuf.buf = data + dataBuf.len = data.len + + var bytesReceived, flags: DWord + var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) + ol.data = TCompletionData(sock: socket, cb: + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) + ) + + let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, + flags, cast[POverlapped](ol), nil) + if ret == -1: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc(ol) + else: + retFuture.complete(0) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. + return retFuture + + proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): + PFuture[tuple[address: string, client: TSocketHandle]] = + ## Accepts a new connection. Returns a future containing the client socket + ## corresponding to that connection and the remote address of the client. + ## The future will complete when the connection is successfully accepted. + + var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() + + var clientSock = socket() + if clientSock == OSInvalidSocket: osError(osLastError()) + + const lpOutputLen = 1024 + var lpOutputBuf = newString(lpOutputLen) + var dwBytesReceived: DWORD + let dwReceiveDataLength = 0.DWORD # We don't want any data to be read. + let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16) + let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16) + + template completeAccept(): stmt {.immediate, dirty.} = + var listenSock = socket + let setoptRet = setsockopt(clientSock, SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, addr listenSock, + sizeof(listenSock).TSockLen) + if setoptRet != 0: osError(osLastError()) + + var LocalSockaddr, RemoteSockaddr: ptr TSockAddr + var localLen, remoteLen: int32 + getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength, + dwLocalAddressLength, dwRemoteAddressLength, + addr LocalSockaddr, addr localLen, + addr RemoteSockaddr, addr remoteLen) + # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186 + retFuture.complete( + (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr), + client: clientSock) + ) + + var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) + ol.data = TCompletionData(sock: socket, cb: + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + completeAccept() + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) + ) + + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx + let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0], + dwReceiveDataLength, + dwLocalAddressLength, + dwRemoteAddressLength, + addr dwBytesReceived, cast[POverlapped](ol)) + + if not ret: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc(ol) + else: + completeAccept() + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. + + return retFuture + + initAll() +else: + import selectors + from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK + type + TCallback = proc (sock: TSocketHandle): bool {.closure.} + + PData* = ref object of PObject + sock: TSocketHandle + readCBs: seq[TCallback] + writeCBs: seq[TCallback] + + PDispatcher* = ref object + selector: PSelector + + proc newDispatcher*(): PDispatcher = + new result + result.selector = newSelector() + + proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) = + assert sock in p.selector + echo("Update: ", events) + if events == {}: + discard p.selector.unregister(sock) + else: + discard p.selector.update(sock, events) + + proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = + if sock notin p.selector: + var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[]) + p.selector.register(sock, {EvRead}, data.PObject) + else: + p.selector[sock].data.PData.readCBs.add(cb) + p.update(sock, p.selector[sock].events + {EvRead}) + + proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = + if sock notin p.selector: + var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb]) + p.selector.register(sock, {EvWrite}, data.PObject) + else: + p.selector[sock].data.PData.writeCBs.add(cb) + p.update(sock, p.selector[sock].events + {EvWrite}) + + proc poll*(p: PDispatcher, timeout = 500) = + for info in p.selector.select(timeout): + let data = PData(info.key.data) + assert data.sock == info.key.fd + echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events) + + if EvRead in info.events: + var newReadCBs: seq[TCallback] = @[] + for cb in data.readCBs: + if not cb(data.sock): + # Callback wants to be called again. + newReadCBs.add(cb) + data.readCBs = newReadCBs + + if EvWrite in info.events: + var newWriteCBs: seq[TCallback] = @[] + for cb in data.writeCBs: + if not cb(data.sock): + # Callback wants to be called again. + newWriteCBs.add(cb) + data.writeCBs = newWriteCBs + + var newEvents: set[TEvent] + if data.readCBs.len != 0: newEvents = {EvRead} + if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} + p.update(data.sock, newEvents) + + proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, + af = AF_INET): PFuture[int] = + var retFuture = newFuture[int]() + + proc cb(sock: TSocketHandle): bool = + # We have connected. + retFuture.complete(0) + return true + + var aiList = getAddrInfo(address, port, af) + var success = false + var lastError: TOSErrorCode + var it = aiList + while it != nil: + var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen) + if ret == 0: + # Request to connect completed immediately. + success = true + retFuture.complete(0) + break + else: + lastError = osLastError() + if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: + success = true + addWrite(p, socket, cb) + break + else: + success = false + it = it.ai_next + + dealloc(aiList) + if not success: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + return retFuture + + proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + flags: int = 0): PFuture[string] = + var retFuture = newFuture[string]() + + var readBuffer = newString(size) + var sizeRead = 0 + + proc cb(sock: TSocketHandle): bool = + result = true + let netSize = size - sizeRead + let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint) + if res < 0: + let lastError = osLastError() + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + elif res == 0: + # Disconnected + if sizeRead == 0: + retFuture.complete("") + else: + readBuffer.setLen(sizeRead) + retFuture.complete(readBuffer) + else: + sizeRead.inc(res) + if res != netSize: + result = false # We want to read all the data requested. + else: + retFuture.complete(readBuffer) + + addRead(p, socket, cb) + return retFuture + + proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = + var retFuture = newFuture[int]() + + var written = 0 + + proc cb(sock: TSocketHandle): bool = + result = true + let netSize = data.len-written + var d = data.cstring + let res = send(sock, addr d[written], netSize, 0.cint) + if res < 0: + let lastError = osLastError() + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + else: + written.inc(res) + if res != netSize: + result = false # We still have data to send. + else: + retFuture.complete(0) + addWrite(p, socket, cb) + return retFuture + + + proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): + PFuture[tuple[address: string, client: TSocketHandle]] = + var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() + proc cb(sock: TSocketHandle): bool = + result = true + var sockAddress: Tsockaddr_in + var addrLen = sizeof(sockAddress).TSocklen + var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)), + addr(addrLen)) + if client == osInvalidSocket: + let lastError = osLastError() + assert lastError.int32 notin {EWOULDBLOCK, EAGAIN} + if lastError.int32 == EINTR: + return false + else: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client)) + addRead(p, socket, cb) + return retFuture + +proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] = + ## Accepts a new connection. Returns a future containing the client socket + ## corresponding to that connection. + ## The future will complete when the connection is successfully accepted. + var retFut = newFuture[TSocketHandle]() + var fut = p.acceptAddr(socket) + fut.callback = + proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = + assert future.finished + if future.failed: + retFut.fail(future.error) + else: + retFut.complete(future.read.client) + return retFut + +# -- Await Macro + +template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} = + proc cbName {.closure.} = + if not varNameIterSym.finished: + var next = varNameIterSym() + if next == nil: + assert retFutureSym.finished, "Async procedure's return Future was not finished." + else: + next.callback = cbName + +template createVar(futSymName: string, asyncProc: PNimrodNode, + valueReceiver: expr) {.immediate, dirty.} = + # TODO: Used template here due to bug #926 + result = newNimNode(nnkStmtList) + var futSym = newIdentNode(futSymName) #genSym(nskVar, "future") + result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y + result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x> + valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read + +proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = + result = node + case node.kind + of nnkReturnStmt: + result = newNimNode(nnkStmtList) + result.add newCall(newIdentNode("complete"), retFutureSym, + if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0]) + result.add newNimNode(nnkYieldStmt).add(newNilLit()) + of nnkCommand: + if node[0].ident == !"await": + case node[1].kind + of nnkIdent: + # await x + result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x + of nnkCall: + # await foo(p, x) + var futureValue: PNimrodNode + createVar("future" & $node[1][0].toStrLit, node[1], futureValue) + result.add futureValue + else: + error("Invalid node kind in 'await', got: " & $node[1].kind) + elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and + node[1][0].ident == !"await": + # foo await x + var newCommand = node + createVar("future" & $node[0].ident, node[1][0], newCommand[1]) + result.add newCommand + + of nnkVarSection, nnkLetSection: + case node[0][2].kind + of nnkCommand: + if node[0][2][0].ident == !"await": + # var x = await y + var newVarSection = node # TODO: Should this use copyNimNode? + createVar("future" & $node[0][0].ident, node[0][2][1], + newVarSection[0][2]) + result.add newVarSection + else: discard + of nnkAsgn: + case node[1].kind + of nnkCommand: + if node[1][0].ident == !"await": + # x = await y + var newAsgn = node + createVar("future" & $node[0].ident, node[1][1], newAsgn[1]) + result.add newAsgn + else: discard + of nnkDiscardStmt: + # discard await x + if node[0][0].ident == !"await": + var dummy = newNimNode(nnkStmtList) + createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy) + else: discard + + for i in 0 .. <result.len: + result[i] = processBody(result[i], retFutureSym) + #echo(treeRepr(result)) + +proc getName(node: PNimrodNode): string {.compileTime.} = + case node.kind + of nnkPostfix: + return $node[1].ident + of nnkIdent: + return $node.ident + else: + assert false + +macro async*(prc: stmt): stmt {.immediate.} = + expectKind(prc, nnkProcDef) + + hint("Processing " & prc[0].getName & " as an async proc.") + + # Verify that the return type is a PFuture[T] + if prc[3][0].kind == nnkIdent: + error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'") + elif prc[3][0].kind == nnkBracketExpr: + if $prc[3][0][0] != "PFuture": + error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'") + + # TODO: Why can't I use genSym? I get illegal capture errors for Syms. + # TODO: It seems genSym is broken. Change all usages back to genSym when fixed + + var outerProcBody = newNimNode(nnkStmtList) + + # -> var retFuture = newFuture[T]() + var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture") + outerProcBody.add( + newVarStmt(retFutureSym, + newCall( + newNimNode(nnkBracketExpr).add( + newIdentNode("newFuture"), + prc[3][0][1])))) # Get type from return type of this proc. + + # -> iterator nameIter(): PFutureBase {.closure.} = + # -> var result: T + # -> <proc_body> + # -> complete(retFuture, result) + var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") + var procBody = prc[6].processBody(retFutureSym) + procBody.insert(0, newNimNode(nnkVarSection).add( + newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T + procBody.add( + newCall(newIdentNode("complete"), + retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) + + var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], + procBody, nnkIteratorDef) + closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure")) + outerProcBody.add(closureIterator) + + # -> var nameIterVar = nameIter + # -> var first = nameIterVar() + var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") + var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) + outerProcBody.add varNameIter + var varFirstSym = genSym(nskVar, "first") + var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym)) + outerProcBody.add varFirst + + # -> createCb(cb, nameIter, retFuture) + var cbName = newIdentNode("cb") + var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym) + outerProcBody.add procCb + + # -> first.callback = cb + outerProcBody.add newAssignment( + newDotExpr(varFirstSym, newIdentNode("callback")), + cbName) + + # -> return retFuture + outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym) + + result = prc + + # Remove the 'async' pragma. + for i in 0 .. <result[4].len: + if result[4][i].ident == !"async": + result[4].del(i) + + result[6] = outerProcBody + + echo(toStrLit(result)) + +proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} = + ## Reads a line of data from ``socket``. Returned future will complete once + ## a full line is read or an error occurs. + ## + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + + template addNLIfEmpty(): stmt = + if result.len == 0: + result.add("\c\L") + + result = "" + var c = "" + while true: + c = await p.recv(socket, 1) + if c.len == 0: + return + if c == "\r": + c = await p.recv(socket, 1, MSG_PEEK) + if c.len > 0 and c == "\L": + discard await p.recv(socket, 1) + addNLIfEmpty() + return + elif c == "\L": + addNLIfEmpty() + return + add(result.string, c) + +when isMainModule: + + var p = newDispatcher() + var sock = socket() + sock.setBlocking false + + + when false: + # Await tests + proc main(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "irc.freenode.net", TPort(6667)) + while true: + var line = await p.recvLine(sock) + echo("Line is: ", line.repr) + if line == "": + echo "Disconnected" + break + + proc peekTest(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "localhost", TPort(6667)) + while true: + var line = await p.recv(sock, 1, MSG_PEEK) + var line2 = await p.recv(sock, 1) + echo(line.repr) + echo(line2.repr) + echo("---") + if line2 == "": break + sleep(500) + + var f = main(p) + + + else: + when false: + + var f = p.connect(sock, "irc.freenode.org", TPort(6667)) + f.callback = + proc (future: PFuture[int]) = + echo("Connected in future!") + echo(future.read) + for i in 0 .. 50: + var recvF = p.recv(sock, 10) + recvF.callback = + proc (future: PFuture[string]) = + echo("Read ", future.read.len, ": ", future.read.repr) + + else: + + sock.bindAddr(TPort(6667)) + sock.listen() + proc onAccept(future: PFuture[TSocketHandle]) = + echo "Accepted" + var t = p.send(future.read, "test\c\L") + t.callback = + proc (future: PFuture[int]) = + echo(future.read) + + var f = p.accept(sock) + f.callback = onAccept + + var f = p.accept(sock) + f.callback = onAccept + + while true: + p.poll() + + + + + + + + \ No newline at end of file diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index e6ab617e5..ad3fe7218 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -241,3 +241,7 @@ proc `<=`*[A](s, t: TSet[A]): bool = proc `==`*[A](s, t: TSet[A]): bool = s.counter == t.counter and s <= t + +proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = + result = initSet[B]() + for item in data: result.incl(op(item)) diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 3ed00fdb2..889912052 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -14,7 +14,7 @@ type TLibHandle* = pointer ## a handle to a dynamically loaded library -proc loadLib*(path: string): TLibHandle +proc loadLib*(path: string, global_symbols=false): TLibHandle ## loads a library from `path`. Returns nil if the library could not ## be loaded. @@ -53,6 +53,7 @@ when defined(posix): # var RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int + RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".} proc dlopen(path: CString, mode: int): TLibHandle {. @@ -60,7 +61,10 @@ when defined(posix): proc dlsym(lib: TLibHandle, name: cstring): pointer {. importc, header: "<dlfcn.h>".} - proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW) + proc loadLib(path: string, global_symbols=false): TLibHandle = + var flags = RTLD_NOW + if global_symbols: flags = flags or RTLD_GLOBAL + return dlopen(path, flags) proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW) proc unloadLib(lib: TLibHandle) = dlclose(lib) proc symAddr(lib: TLibHandle, name: cstring): pointer = @@ -81,7 +85,7 @@ elif defined(windows) or defined(dos): proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. importc: "GetProcAddress", header: "<windows.h>", stdcall.} - proc loadLib(path: string): TLibHandle = + proc loadLib(path: string, global_symbols=false): TLibHandle = result = cast[TLibHandle](winLoadLibrary(path)) proc loadLib(): TLibHandle = result = cast[TLibHandle](winLoadLibrary(nil)) diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index 63737d583..b9d6aec7b 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -1,12 +1,17 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # +## **Warning**: This module uses ``immediate`` macros which are known to +## cause problems. Do yourself a favor and import the module +## as ``from htmlgen import nil`` and then fully qualify the macros. +## +## ## This module implements a simple `XML`:idx: and `HTML`:idx: code ## generator. Each commonly used HTML tag has a corresponding macro ## that generates a string with its HTML representation. @@ -15,11 +20,11 @@ ## ## .. code-block:: nimrod ## var nim = "Nimrod" -## echo h1(a(href="http://nimrod-code.org", nim)) +## echo h1(a(href="http://nimrod-lang.org", nim)) ## ## Writes the string:: ## -## <h1><a href="http://nimrod-code.org">Nimrod</a></h1> +## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1> ## import diff --git a/lib/pure/net.nim b/lib/pure/net.nim new file mode 100644 index 000000000..0ec007009 --- /dev/null +++ b/lib/pure/net.nim @@ -0,0 +1,55 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a high-level cross-platform sockets interface. + +import sockets2, os + +type + TSocket* = TSocketHandle + +proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. + tags: [FReadIO].} = + + ## binds an address/port number to a socket. + ## Use address string in dotted decimal form like "a.b.c.d" + ## or leave "" for any address. + + if address == "": + var name: TSockaddr_in + when defined(windows): + name.sin_family = toInt(AF_INET).int16 + else: + name.sin_family = toInt(AF_INET) + name.sin_port = htons(int16(port)) + name.sin_addr.s_addr = htonl(INADDR_ANY) + if bindAddr(socket, cast[ptr TSockAddr](addr(name)), + sizeof(name).TSocklen) < 0'i32: + osError(osLastError()) + else: + var aiList = getAddrInfo(address, port, AF_INET) + if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: + dealloc(aiList) + osError(osLastError()) + dealloc(aiList) + +proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} = + ## Sets blocking mode on socket + when defined(Windows): + var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking + if ioctlsocket(s, FIONBIO, addr(mode)) == -1: + osError(osLastError()) + else: # BSD sockets + var x: int = fcntl(s, F_GETFL, 0) + if x == -1: + osError(osLastError()) + else: + var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK + if fcntl(s, F_SETFL, mode) == -1: + osError(osLastError()) \ No newline at end of file diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 02f0366cd..3d0cc2154 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -67,7 +67,7 @@ when withThreads: proc hookAux(st: TStackTrace, costs: int) = # this is quite performance sensitive! - when withThreads: Acquire profilingLock + when withThreads: acquire profilingLock inc totalCalls var last = high(st) while last > 0 and isNil(st[last]): dec last @@ -106,7 +106,7 @@ proc hookAux(st: TStackTrace, costs: int) = h = ((5 * h) + 1) and high(profileData) inc chain maxChainLen = max(maxChainLen, chain) - when withThreads: Release profilingLock + when withThreads: release profilingLock when defined(memProfiler): const diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bb70f28b6..bfecc569a 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1037,7 +1037,10 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", ## the process has finished. To execute a program without having a ## shell involved, use the `execProcess` proc of the `osproc` ## module. - result = c_system(command) shr 8 + when defined(linux): + result = c_system(command) shr 8 + else: + result = c_system(command) # Environment handling cannot be put into RTL, because the ``envPairs`` # iterator depends on ``environment``. @@ -1189,7 +1192,8 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} = res = findFirstFile(pattern, f) if res != -1: while true: - if not skipFindData(f): + if not skipFindData(f) and + (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) == 0'i32: yield splitFile(pattern).dir / extractFilename(getFilename(f)) if findNextFile(res, f) == 0'i32: break findClose(res) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6df85bbc6..582b3c960 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -13,13 +13,16 @@ include "system/inclrtl" import - strutils, os, strtabs, streams, sequtils + strutils, os, strtabs, streams when defined(windows): import winlean else: import posix +when defined(linux): + import linux + type TProcess = object of TObject when defined(windows): @@ -44,7 +47,7 @@ type poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poParentStreams ## use the parent's streams -template poUseShell*: TProcessOption {.deprecated.} = poUsePath +const poUseShell* {.deprecated.} = poUsePath ## Deprecated alias for poUsePath. proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = @@ -165,6 +168,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} = proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, extern: "nosp$1", tags: [].} ## waits for the process to finish and returns `p`'s error code. + ## + ## **Warning**: Be careful when using waitForExit for processes created without + ## poParentStreams because they may fill output buffers, causing deadlock. proc peekExitCode*(p: PProcess): int {.tags: [].} ## return -1 if the process is still running. Otherwise the process' exit code @@ -590,6 +596,23 @@ elif not defined(useNimRtl): copyMem(result[i], addr(x[0]), x.len+1) inc(i) + type TStartProcessData = object + sysCommand: cstring + sysArgs: cstringArray + sysEnv: cstringArray + workingDir: cstring + pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint] + optionPoUsePath: bool + optionPoParentStreams: bool + optionPoStdErrToStdOut: bool + + proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} + proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} + {.push stacktrace: off, profiler: off.} + proc startProcessAfterFork(data: ptr TStartProcessData) {. + tags: [FExecIO, FReadEnv], cdecl.} + {.pop.} + proc startProcess(command: string, workingDir: string = "", args: openArray[string] = [], @@ -604,100 +627,48 @@ elif not defined(useNimRtl): pipe(pStderr) != 0'i32: osError(osLastError()) - var sys_command: string - var sys_args_raw: seq[string] + var sysCommand: string + var sysArgsRaw: seq[string] if poEvalCommand in options: - sys_command = "/bin/sh" - sys_args_raw = @[sys_command, "-c", command] + sysCommand = "/bin/sh" + sysArgsRaw = @[sysCommand, "-c", command] assert args.len == 0 else: - sys_command = command - sys_args_raw = @[command] + sysCommand = command + sysArgsRaw = @[command] for arg in args.items: - sys_args_raw.add arg - - var sys_args = allocCStringArray(sys_args_raw) - finally: deallocCStringArray(sys_args) + sysArgsRaw.add arg var pid: TPid - when defined(posix_spawn) and not defined(useFork): - var attr: Tposix_spawnattr - var fops: Tposix_spawn_file_actions - - template chck(e: expr) = - if e != 0'i32: osError(osLastError()) - - chck posix_spawn_file_actions_init(fops) - chck posix_spawnattr_init(attr) - - var mask: Tsigset - chck sigemptyset(mask) - chck posix_spawnattr_setsigmask(attr, mask) - chck posix_spawnattr_setpgroup(attr, 0'i32) - - chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or - POSIX_SPAWN_SETSIGMASK or - POSIX_SPAWN_SETPGROUP) - - if poParentStreams notin options: - chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx]) - chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx) - chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx]) - chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx) - chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx]) - if poStdErrToStdOut in options: - chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2) - else: - chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2) - - var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env) - var res: cint - # This is incorrect! - if workingDir.len > 0: os.setCurrentDir(workingDir) - if poUsePath in options: - res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env) + + var sysArgs = allocCStringArray(sysArgsRaw) + finally: deallocCStringArray(sysArgs) + + var sysEnv = if env == nil: + envToCStringArray() else: - res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env) - deallocCStringArray(sys_env) - discard posix_spawn_file_actions_destroy(fops) - discard posix_spawnattr_destroy(attr) - chck res + envToCStringArray(env) + + finally: deallocCStringArray(sysEnv) + + var data: TStartProcessData + data.sysCommand = sysCommand + data.sysArgs = sysArgs + data.sysEnv = sysEnv + data.pStdin = pStdin + data.pStdout = pStdout + data.pStderr = pStderr + data.optionPoParentStreams = poParentStreams in options + data.optionPoUsePath = poUsePath in options + data.optionPoStdErrToStdOut = poStdErrToStdOut in options + data.workingDir = workingDir + + when defined(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux): + pid = startProcessAuxSpawn(data) else: - pid = fork() - if pid < 0: osError(osLastError()) - if pid == 0: - ## child process: - - if poParentStreams notin options: - discard close(p_stdin[writeIdx]) - if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError()) - discard close(p_stdout[readIdx]) - if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError()) - discard close(p_stderr[readIdx]) - if poStdErrToStdOut in options: - if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError()) - else: - if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError()) - - # Create a new process group - if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno)) - - if workingDir.len > 0: os.setCurrentDir(workingDir) - - if env == nil: - if poUsePath in options: - discard execvp(sys_command, sys_args) - else: - discard execv(sys_command, sys_args) - else: - var c_env = envToCStringArray(env) - if poUsePath in options: - discard execvpe(sys_command, sys_args, c_env) - else: - discard execve(sys_command, sys_args, c_env) - # too risky to raise an exception here: - quit("execve call failed: " & $strerror(errno)) + pid = startProcessAuxFork(data) + # Parent process. Copy process information. if poEchoCmd in options: echo(command, " ", join(args, " ")) @@ -723,6 +694,137 @@ elif not defined(useNimRtl): discard close(pStdin[readIdx]) discard close(pStdout[writeIdx]) + proc startProcessAuxSpawn(data: TStartProcessData): TPid = + var attr: Tposix_spawnattr + var fops: Tposix_spawn_file_actions + + template chck(e: expr) = + if e != 0'i32: osError(osLastError()) + + chck posix_spawn_file_actions_init(fops) + chck posix_spawnattr_init(attr) + + var mask: Tsigset + chck sigemptyset(mask) + chck posix_spawnattr_setsigmask(attr, mask) + chck posix_spawnattr_setpgroup(attr, 0'i32) + + chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or + POSIX_SPAWN_SETSIGMASK or + POSIX_SPAWN_SETPGROUP) + + if not data.optionPoParentStreams: + chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx]) + chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx) + chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx]) + chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx) + chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx]) + if data.optionPoStdErrToStdOut: + chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2) + else: + chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2) + + var res: cint + # FIXME: chdir is global to process + if data.workingDir.len > 0: + setCurrentDir($data.workingDir) + var pid: TPid + + if data.optionPoUsePath: + res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) + else: + res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) + + discard posix_spawn_file_actions_destroy(fops) + discard posix_spawnattr_destroy(attr) + chck res + return pid + + proc startProcessAuxFork(data: TStartProcessData): TPid = + if pipe(data.pErrorPipe) != 0: + osError(osLastError()) + + finally: + discard close(data.pErrorPipe[readIdx]) + + var pid: TPid + var dataCopy = data + + when defined(useClone): + const stackSize = 65536 + let stackEnd = cast[clong](alloc(stackSize)) + let stack = cast[pointer](stackEnd + stackSize) + let fn: pointer = startProcessAfterFork + pid = clone(fn, stack, + cint(CLONE_VM or CLONE_VFORK or SIGCHLD), + pointer(addr dataCopy), nil, nil, nil) + discard close(data.pErrorPipe[writeIdx]) + dealloc(stack) + else: + pid = fork() + if pid == 0: + startProcessAfterFork(addr(dataCopy)) + exitnow(1) + + discard close(data.pErrorPipe[writeIdx]) + if pid < 0: osError(osLastError()) + + var error: cint + let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) + if sizeRead == sizeof(error): + osError($strerror(error)) + + return pid + + {.push stacktrace: off, profiler: off.} + proc startProcessFail(data: ptr TStartProcessData) = + var error: cint = errno + discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) + exitnow(1) + + when defined(macosx): + var environ {.importc.}: cstringArray + + proc startProcessAfterFork(data: ptr TStartProcessData) = + # Warning: no GC here! + # Or anythink that touches global structures - all called nimrod procs + # must be marked with noStackFrame. Inspect C code after making changes. + if not data.optionPoParentStreams: + discard close(data.pStdin[writeIdx]) + if dup2(data.pStdin[readIdx], readIdx) < 0: + startProcessFail(data) + discard close(data.pStdout[readIdx]) + if dup2(data.pStdout[writeIdx], writeIdx) < 0: + startProcessFail(data) + discard close(data.pStderr[readIdx]) + if data.optionPoStdErrToStdOut: + if dup2(data.pStdout[writeIdx], 2) < 0: + startProcessFail(data) + else: + if dup2(data.pStderr[writeIdx], 2) < 0: + startProcessFail(data) + + if data.workingDir.len > 0: + if chdir(data.workingDir) < 0: + startProcessFail(data) + + discard close(data.pErrorPipe[readIdx]) + discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC) + + if data.optionPoUsePath: + when defined(macosx): + # MacOSX doesn't have execvpe, so we need workaround. + # On MacOSX we can arrive here only from fork, so this is safe: + environ = data.sysEnv + discard execvp(data.sysCommand, data.sysArgs) + else: + discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) + else: + discard execve(data.sysCommand, data.sysArgs, data.sysEnv) + + startProcessFail(data) + {.pop} + proc close(p: PProcess) = if p.inStream != nil: close(p.inStream) if p.outStream != nil: close(p.outStream) @@ -791,7 +893,10 @@ elif not defined(useNimRtl): proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} proc execCmd(command: string): int = - result = csystem(command) shr 8 + when defined(linux): + result = csystem(command) shr 8 + else: + result = csystem(command) proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) = FD_ZERO(fd) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 70b617393..68b1ab223 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string = while i < s.len: c.ml = 0 var L = rawMatch(s, pattern, i, c) - if L < 0: break - yield substr(s, i, i+L-1) - inc(i, L) + if L < 0: + inc(i, 1) + else: + yield substr(s, i, i+L-1) + inc(i, L) proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. nosideEffect, rtl, extern: "npegs$1".} = diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim new file mode 100644 index 000000000..6482a01a6 --- /dev/null +++ b/lib/pure/selectors.nim @@ -0,0 +1,250 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# TODO: Docs. + +import tables, os, unsigned, hashes + +when defined(linux): import posix, epoll +elif defined(windows): import winlean + +proc hash*(x: TSocketHandle): THash {.borrow.} + +type + TEvent* = enum + EvRead, EvWrite + + PSelectorKey* = ref object + fd*: TSocketHandle + events*: set[TEvent] ## The events which ``fd`` listens for. + data*: PObject ## User object. + + TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] + +when defined(linux) or defined(nimdoc): + type + PSelector* = ref object + epollFD: cint + events: array[64, ptr epoll_event] + fds: TTable[TSocketHandle, PSelectorKey] + + proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = + if EvRead in events: + result.events = EPOLLIN + if EvWrite in events: + result.events = result.events or EPOLLOUT + result.data.fd = fd.cint + + proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], + data: PObject): PSelectorKey {.discardable.} = + ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent + ## ``events``. + if s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor already exists.") + + var event = createEventStruct(events, fd) + + if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: + OSError(OSLastError()) + + var key = PSelectorKey(fd: fd, events: events, data: data) + + s.fds[fd] = key + result = key + + proc update*(s: PSelector, fd: TSocketHandle, + events: set[TEvent]): PSelectorKey {.discardable.} = + ## Updates the events which ``fd`` wants notifications for. + if not s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor not found.") + var event = createEventStruct(events, fd) + + s.fds[fd].events = events + echo("About to update") + if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + OSError(OSLastError()) + echo("finished updating") + result = s.fds[fd] + + proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = + if not s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor not found.") + if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: + OSError(OSLastError()) + result = s.fds[fd] + s.fds.del(fd) + + proc close*(s: PSelector) = + if s.epollFD.close() != 0: OSError(OSLastError()) + dealloc(addr s.events) # TODO: Test this + + proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = + ## + ## The ``events`` field of the returned ``key`` contains the original events + ## for which the ``fd`` was bound. This is contrary to the ``events`` field + ## of the ``TReadyInfo`` tuple which determines which events are ready + ## on the ``fd``. + result = @[] + + let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint) + if evNum < 0: OSError(OSLastError()) + if evNum == 0: return @[] + for i in 0 .. <evNum: + var evSet: set[TEvent] = {} + if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} + if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} + + let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle] + result.add((selectorKey, evSet)) + + proc newSelector*(): PSelector = + new result + result.epollFD = epoll_create(64) + result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64)) + result.fds = initTable[TSocketHandle, PSelectorKey]() + if result.epollFD < 0: + OSError(OSLastError()) + + proc contains*(s: PSelector, fd: TSocketHandle): bool = + ## Determines whether selector contains a file descriptor. + return s.fds.hasKey(fd) + + proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = + ## Retrieves the selector key for ``fd``. + return s.fds[fd] + +elif defined(windows): + type + PSelector* = ref object + fds: TTable[TSocketHandle, PSelectorKey] + + proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], + data: PObject): PSelectorKey {.discardable.} = + if s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor already exists.") + var sk = PSelectorKey(fd: fd, events: events, data: data) + s.fds[fd] = sk + result = sk + + proc update*(s: PSelector, fd: TSocketHandle, + events: set[TEvent]): PSelectorKey {.discardable.} = + ## Updates the events which ``fd`` wants notifications for. + if not s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor not found.") + + s.fds[fd].events = events + result = s.fds[fd] + + proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = + result = s.fds[fd] + s.fds.del(fd) + + proc close*(s: PSelector) = nil + + proc timeValFromMilliseconds(timeout: int): TTimeVal = + if timeout != -1: + var seconds = timeout div 1000 + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + + proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey], + m: var int) = + FD_ZERO(rd); FD_ZERO(wr) + for k, v in pairs(fds): + if EvRead in v.events: + m = max(m, int(k)) + FD_SET(k, rd) + if EvWrite in v.events: + m = max(m, int(k)) + FD_SET(k, wr) + + proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]): + seq[TReadyInfo] = + result = @[] + for k, v in pairs(fds): + var events: set[TEvent] = {} + if FD_ISSET(k, rd) != 0'i32: + events = events + {EvRead} + if FD_ISSET(k, wr) != 0'i32: + events = events + {EvWrite} + result.add((v, events)) + + proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500): + seq[TReadyInfo] = + var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout) + + var rd, wr: TFdSet + var m = 0 + createFdSet(rd, wr, fds, m) + + var retCode = 0 + if timeout != -1: + retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, addr(tv))) + else: + retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, nil)) + + if retCode < 0: + OSError(OSLastError()) + elif retCode == 0: + return @[] + else: + return getReadyFDs(rd, wr, fds) + + proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = + result = select(s.fds, timeout) + + proc newSelector*(): PSelector = + new result + result.fds = initTable[TSocketHandle, PSelectorKey]() + + proc contains*(s: PSelector, fd: TSocketHandle): bool = + return s.fds.hasKey(fd) + + proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = + return s.fds[fd] + +elif defined(bsd) or defined(macosx): + # TODO: kqueue + {.error: "Sorry your platform is not supported yet.".} +else: + {.error: "Sorry your platform is not supported.".} + +when isMainModule: + # Select() + import sockets + type + PSockWrapper = ref object of PObject + sock: TSocket + + var sock = socket() + sock.setBlocking(false) + sock.connect("irc.freenode.net", TPort(6667)) + + var selector = newSelector() + var data = PSockWrapper(sock: sock) + let key = selector.register(sock.getFD, {EvWrite}, data) + var i = 0 + while true: + let ready = selector.select(1000) + echo ready.len + if ready.len > 0: echo ready[0].events + i.inc + if i == 6: + assert selector.unregister(sock.getFD).fd == sock.getFD + selector.close() + break + + + + + + + + + \ No newline at end of file diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim new file mode 100644 index 000000000..3542a0694 --- /dev/null +++ b/lib/pure/sockets2.nim @@ -0,0 +1,213 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a low-level cross-platform sockets interface. Look +## at the ``net`` module for the higher-level version. + +import unsigned, os + +when hostos == "solaris": + {.passl: "-lsocket -lnsl".} + +when defined(Windows): + import winlean +else: + import posix + export fcntl, F_GETFL, O_NONBLOCK, F_SETFL + +export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, + inet_ntoa, recv, `==`, connect, send, accept + +type + + TPort* = distinct uint16 ## port type + + TDomain* = enum ## domain, which specifies the protocol family of the + ## created socket. Other domains than those that are listed + ## here are unsupported. + AF_UNIX, ## for local socket (using a file). Unsupported on Windows. + AF_INET = 2, ## for network protocol IPv4 or + AF_INET6 = 23 ## for network protocol IPv6. + + TType* = enum ## second argument to `socket` proc + SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets + SOCK_DGRAM = 2, ## datagram service or Datagram Sockets + SOCK_RAW = 3, ## raw protocols atop the network layer. + SOCK_SEQPACKET = 5 ## reliable sequenced packet service + + TProtocol* = enum ## third argument to `socket` proc + IPPROTO_TCP = 6, ## Transmission control protocol. + IPPROTO_UDP = 17, ## User datagram protocol. + IPPROTO_IP, ## Internet protocol. Unsupported on Windows. + IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. + IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. + IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. + + TServent* {.pure, final.} = object ## information about a service + name*: string + aliases*: seq[string] + port*: TPort + proto*: string + + Thostent* {.pure, final.} = object ## information about a given host + name*: string + aliases*: seq[string] + addrtype*: TDomain + length*: int + addrList*: seq[string] + +when defined(windows): + let + osInvalidSocket* = winlean.INVALID_SOCKET + + const + IOCPARM_MASK* = 127 + IOC_IN* = int(-2147483648) + FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or + (102 shl 8) or 126 + + proc ioctlsocket*(s: TSocketHandle, cmd: clong, + argptr: ptr clong): cint {. + stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} +else: + let + osInvalidSocket* = posix.INVALID_SOCKET + +proc `==`*(a, b: TPort): bool {.borrow.} + ## ``==`` for ports. + +proc `$`*(p: TPort): string {.borrow.} + ## returns the port number as a string + +proc toInt*(domain: TDomain): cint + ## Converts the TDomain enum to a platform-dependent ``cint``. + +proc toInt*(typ: TType): cint + ## Converts the TType enum to a platform-dependent ``cint``. + +proc toInt*(p: TProtocol): cint + ## Converts the TProtocol enum to a platform-dependent ``cint``. + +when defined(posix): + proc toInt(domain: TDomain): cint = + case domain + of AF_UNIX: result = posix.AF_UNIX + of AF_INET: result = posix.AF_INET + of AF_INET6: result = posix.AF_INET6 + else: discard + + proc toInt(typ: TType): cint = + case typ + of SOCK_STREAM: result = posix.SOCK_STREAM + of SOCK_DGRAM: result = posix.SOCK_DGRAM + of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET + of SOCK_RAW: result = posix.SOCK_RAW + else: discard + + proc toInt(p: TProtocol): cint = + case p + of IPPROTO_TCP: result = posix.IPPROTO_TCP + of IPPROTO_UDP: result = posix.IPPROTO_UDP + of IPPROTO_IP: result = posix.IPPROTO_IP + of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 + of IPPROTO_RAW: result = posix.IPPROTO_RAW + of IPPROTO_ICMP: result = posix.IPPROTO_ICMP + else: discard + +else: + proc toInt(domain: TDomain): cint = + result = toU16(ord(domain)) + + proc toInt(typ: TType): cint = + result = cint(ord(typ)) + + proc toInt(p: TProtocol): cint = + result = cint(ord(p)) + + +proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, + protocol: TProtocol = IPPROTO_TCP): TSocketHandle = + ## Creates a new socket; returns `InvalidSocket` if an error occurs. + + # TODO: The function which will use this will raise EOS. + socket(toInt(domain), toInt(typ), toInt(protocol)) + +proc close*(socket: TSocketHandle) = + ## closes a socket. + when defined(windows): + discard winlean.closeSocket(socket) + else: + discard posix.close(socket) + # TODO: These values should not be discarded. An EOS should be raised. + # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times + +proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint = + result = bindSocket(socket, name, namelen) + +proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} = + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the + ## queue of pending connections. + when defined(windows): + if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError()) + else: + if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError()) + +proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM, + prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo = + ## + ## + ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``! + var hints: TAddrInfo + result = nil + hints.ai_family = toInt(af) + hints.ai_socktype = toInt(typ) + hints.ai_protocol = toInt(prot) + var gaiResult = getAddrInfo(address, $port, addr(hints), result) + if gaiResult != 0'i32: + when defined(windows): + OSError(OSLastError()) + else: + raise newException(EOS, $gai_strerror(gaiResult)) + +proc dealloc*(ai: ptr TAddrInfo) = + freeaddrinfo(ai) + +proc ntohl*(x: int32): int32 = + ## Converts 32-bit integers from network to host byte order. + ## On machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + 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) + +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. + when cpuEndian == bigEndian: result = x + else: result = (x shr 8'i16) or (x shl 8'i16) + +proc htonl*(x: int32): int32 = + ## 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. + result = sockets2.ntohl(x) + +proc htons*(x: int16): int16 = + ## Converts 16-bit positive 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. + result = sockets2.ntohs(x) + +when defined(Windows): + var wsa: TWSADATA + if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 6186fcad8..2fce235e2 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months, result.months = months result.years = years -proc isLeapYear(year: int): bool = +proc isLeapYear*(year: int): bool = + ## returns true if ``year`` is a leap year + if year mod 400 == 0: return true elif year mod 100 == 0: @@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool = else: return false -proc getDaysInMonth(month: TMonth, year: int): int = +proc getDaysInMonth*(month: TMonth, year: int): int = + ## gets the amount of days in a ``month`` of a ``year`` + # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month case month of mFeb: result = if isLeapYear(year): 29 else: 28 @@ -553,6 +557,119 @@ proc `$`*(m: TMonth): string = "November", "December"] return lookup[m] +proc format_token(info: TTimeInfo, token: string, buf: var string) = + ## Helper of the format proc to parse individual tokens. + ## + ## Pass the found token in the user input string, and the buffer where the + ## final string is being built. This has to be a var value because certain + ## formatting tokens require modifying the previous characters. + case token + of "d": + buf.add($info.monthday) + of "dd": + if info.monthday < 10: + buf.add("0") + buf.add($info.monthday) + of "ddd": + buf.add(($info.weekday)[0 .. 2]) + of "dddd": + buf.add($info.weekday) + of "h": + buf.add($(if info.hour > 12: info.hour - 12 else: info.hour)) + of "hh": + let amerHour = if info.hour > 12: info.hour - 12 else: info.hour + if amerHour < 10: + buf.add('0') + buf.add($amerHour) + of "H": + buf.add($info.hour) + of "HH": + if info.hour < 10: + buf.add('0') + buf.add($info.hour) + of "m": + buf.add($info.minute) + of "mm": + if info.minute < 10: + buf.add('0') + buf.add($info.minute) + of "M": + buf.add($(int(info.month)+1)) + of "MM": + if info.month < mOct: + buf.add('0') + buf.add($(int(info.month)+1)) + of "MMM": + buf.add(($info.month)[0..2]) + of "MMMM": + buf.add($info.month) + of "s": + buf.add($info.second) + of "ss": + if info.second < 10: + buf.add('0') + buf.add($info.second) + of "t": + if info.hour >= 12: + buf.add('P') + else: buf.add('A') + of "tt": + if info.hour >= 12: + buf.add("PM") + else: buf.add("AM") + of "y": + var fr = ($info.year).len()-1 + if fr < 0: fr = 0 + buf.add(($info.year)[fr .. ($info.year).len()-1]) + of "yy": + var fr = ($info.year).len()-2 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyy": + var fr = ($info.year).len()-3 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyyy": + var fr = ($info.year).len()-4 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyyyy": + var fr = ($info.year).len()-5 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear + buf.add(fyear) + of "z": + let hrs = (info.timezone div 60) div 60 + buf.add($hrs) + of "zz": + let hrs = (info.timezone div 60) div 60 + + buf.add($hrs) + if hrs.abs < 10: + var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0)) + buf.insert("0", atIndex) + of "zzz": + let hrs = (info.timezone div 60) div 60 + + buf.add($hrs & ":00") + if hrs.abs < 10: + var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0)) + buf.insert("0", atIndex) + of "ZZZ": + buf.add(info.tzname) + of "": + discard + else: + raise newException(EInvalidValue, "Invalid format string: " & token) + + proc format*(info: TTimeInfo, f: string): string = ## This function formats `info` as specified by `f`. The following format ## specifiers are available: @@ -587,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string = ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` ## ========== ================================================================================= ================================================ ## - ## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. - ## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` + ## Other strings can be inserted by putting them in ``''``. For example + ## ``hh'->'mm`` will give ``01->56``. The following characters can be + ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` + ## ``,``. However you don't need to necessarily separate format specifiers, a + ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. result = "" var i = 0 @@ -596,112 +716,8 @@ proc format*(info: TTimeInfo, f: string): string = while true: case f[i] of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': - case currentF - of "d": - result.add($info.monthday) - of "dd": - if info.monthday < 10: - result.add("0") - result.add($info.monthday) - of "ddd": - result.add(($info.weekday)[0 .. 2]) - of "dddd": - result.add($info.weekday) - of "h": - result.add($(if info.hour > 12: info.hour - 12 else: info.hour)) - of "hh": - let amerHour = if info.hour > 12: info.hour - 12 else: info.hour - if amerHour < 10: - result.add('0') - result.add($amerHour) - of "H": - result.add($info.hour) - of "HH": - if info.hour < 10: - result.add('0') - result.add($info.hour) - of "m": - result.add($info.minute) - of "mm": - if info.minute < 10: - result.add('0') - result.add($info.minute) - of "M": - result.add($(int(info.month)+1)) - of "MM": - if info.month < mOct: - result.add('0') - result.add($(int(info.month)+1)) - of "MMM": - result.add(($info.month)[0..2]) - of "MMMM": - result.add($info.month) - of "s": - result.add($info.second) - of "ss": - if info.second < 10: - result.add('0') - result.add($info.second) - of "t": - if info.hour >= 12: - result.add('P') - else: result.add('A') - of "tt": - if info.hour >= 12: - result.add("PM") - else: result.add("AM") - of "y": - var fr = ($info.year).len()-1 - if fr < 0: fr = 0 - result.add(($info.year)[fr .. ($info.year).len()-1]) - of "yy": - var fr = ($info.year).len()-2 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear - result.add(fyear) - of "yyy": - var fr = ($info.year).len()-3 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear - result.add(fyear) - of "yyyy": - var fr = ($info.year).len()-4 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear - result.add(fyear) - of "yyyyy": - var fr = ($info.year).len()-5 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear - result.add(fyear) - of "z": - let hrs = (info.timezone div 60) div 60 - result.add($hrs) - of "zz": - let hrs = (info.timezone div 60) div 60 - - result.add($hrs) - if hrs.abs < 10: - var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0)) - result.insert("0", atIndex) - of "zzz": - let hrs = (info.timezone div 60) div 60 - - result.add($hrs & ":00") - if hrs.abs < 10: - var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0)) - result.insert("0", atIndex) - of "ZZZ": - result.add(info.tzname) - of "": - discard - else: - raise newException(EInvalidValue, "Invalid format string: " & currentF) - + format_token(info, currentF, result) + currentF = "" if f[i] == '\0': break @@ -712,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string = inc(i) else: result.add(f[i]) - else: currentF.add(f[i]) + else: + # Check if the letter being added matches previous accumulated buffer. + if currentF.len < 1 or currentF[high(currentF)] == f[i]: + currentF.add(f[i]) + else: + format_token(info, currentF, result) + dec(i) # Move position back to re-process the character separately. + currentF = "" + inc(i) {.pop.} @@ -723,11 +747,15 @@ when isMainModule: var t = getGMTime(fromSeconds(2147483647)) echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") + echo t.format("ddd ddMMMhhmmssZZZyyyy") assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038" + assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038" assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" + + assert t.format("yyyyMMddhhmmss") == "20380119031407" var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & diff --git a/lib/stdlib.babel b/lib/stdlib.babel new file mode 100644 index 000000000..f22598aba --- /dev/null +++ b/lib/stdlib.babel @@ -0,0 +1,6 @@ +[Package] +name = "stdlib" +version = "0.9.0" +author = "Dominik Picheta" +description = "Nimrod's standard library." +license = "MIT" diff --git a/lib/system.nim b/lib/system.nim index e0977d104..171c7b6b8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} = when not defined(niminheritable): {.pragma: inheritable.} +when not defined(nimunion): + {.pragma: unchecked.} const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \ ## "fake variables" like 'var EBADF {.importc.}: cint'. @@ -194,9 +196,10 @@ when not defined(JS): TGenericSeq {.compilerproc, pure, inheritable.} = object len, reserved: int PGenericSeq {.exportc.} = ptr TGenericSeq + UncheckedCharArray {.unchecked.} = array[0..100_000_000, char] # len and space without counting the terminating zero: NimStringDesc {.compilerproc, final.} = object of TGenericSeq - data: array[0..100_000_000, char] + data: UncheckedCharArray NimString = ptr NimStringDesc when not defined(JS) and not defined(NimrodVM): @@ -1559,7 +1562,7 @@ when not defined(NimrodVM): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) else: - proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} = + proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = asm """return `x`""" proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = @@ -1842,7 +1845,7 @@ type len*: int ## length of the inspectable slots when defined(JS): - proc add*(x: var string, y: cstring) {.noStackFrame.} = + proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = asm """ var len = `x`[0].length-1; for (var i = 0; i < `y`.length; ++i) { @@ -2059,8 +2062,10 @@ when not defined(JS): #and not defined(NimrodVM): ## Flushes `f`'s buffer. proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].} - ## Reads all data from the stream `file`. Raises an IO exception - ## in case of an error + ## Reads all data from the stream `file`. + ## + ## Raises an IO exception in case of an error. It is an error if the + ## current file position is not at the beginning of the file. proc readFile*(filename: string): TaintedString {.tags: [FReadIO].} ## Opens a file named `filename` for reading. Then calls `readAll` diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim index d764a6672..d9b3aebac 100644 --- a/lib/system/arithm.nim +++ b/lib/system/arithm.nim @@ -111,7 +111,7 @@ const when asmVersion and not defined(gcc) and not defined(llvm_gcc): # assembler optimized versions for compilers that # have an intel syntax assembler: - proc addInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = # a in eax, and b in edx asm """ mov eax, `a` @@ -121,7 +121,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc subInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` sub eax, `b` @@ -130,7 +130,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc negInt(a: int): int {.compilerProc, noStackFrame.} = + proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` neg eax @@ -139,7 +139,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc divInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` mov ecx, `b` @@ -150,7 +150,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc modInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` mov ecx, `b` @@ -162,7 +162,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): mov eax, edx """ - proc mulInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` mov ecx, `b` diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 820093b3e..b08a6d214 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -802,7 +802,7 @@ when defined(sparc): # For SPARC architecture. # Addresses decrease as the stack grows. while sp <= max: gcMark(gch, sp[]) - sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer)) + sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer)) elif defined(ELATE): {.error: "stack marking code is to be written for this architecture".} diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 4fc5f479b..1720804c4 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -23,9 +23,9 @@ type PCallFrame = ptr TCallFrame TCallFrame {.importc, nodecl, final.} = object prev: PCallFrame - procname: CString + procname: cstring line: int # current line number - filename: CString + filename: cstring var framePtr {.importc, nodecl, volatile.}: PCallFrame @@ -48,7 +48,7 @@ proc getCurrentExceptionMsg*(): string = proc auxWriteStackTrace(f: PCallFrame): string = type - TTempFrame = tuple[procname: CString, line: int] + TTempFrame = tuple[procname: cstring, line: int] var it = f i = 0 @@ -84,7 +84,7 @@ proc rawWriteStackTrace(): string = framePtr = nil proc raiseException(e: ref E_Base, ename: cstring) {. - compilerproc, noStackFrame.} = + compilerproc, asmNoStackFrame.} = e.name = ename if excHandler != nil: excHandler.exc = e @@ -104,7 +104,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {. alert(buf) asm """throw `e`;""" -proc reraiseException() {.compilerproc, noStackFrame.} = +proc reraiseException() {.compilerproc, asmNoStackFrame.} = if excHandler == nil: raise newException(ENoExceptionToReraise, "no exception to reraise") else: @@ -125,7 +125,7 @@ proc raiseIndexError() {.compilerproc, noreturn.} = proc raiseFieldError(f: string) {.compilerproc, noreturn.} = raise newException(EInvalidField, f & " is not accessible") -proc SetConstr() {.varargs, noStackFrame, compilerproc.} = +proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} = asm """ var result = {}; for (var i = 0; i < arguments.length; ++i) { @@ -141,7 +141,7 @@ proc SetConstr() {.varargs, noStackFrame, compilerproc.} = return result; """ -proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = +proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = asm """ var result = []; for (var i = 0; i < `c`.length; ++i) { @@ -151,7 +151,7 @@ proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = return result; """ -proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = +proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = asm """ var len = `s`.length-1; var result = new Array(len); @@ -162,7 +162,7 @@ proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = return result.join(""); """ -proc mnewString(len: int): string {.noStackFrame, compilerproc.} = +proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = asm """ var result = new Array(`len`+1); result[0] = 0; @@ -170,7 +170,7 @@ proc mnewString(len: int): string {.noStackFrame, compilerproc.} = return result; """ -proc SetCard(a: int): int {.compilerproc, noStackFrame.} = +proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = # argument type is a fake asm """ var result = 0; @@ -178,14 +178,14 @@ proc SetCard(a: int): int {.compilerproc, noStackFrame.} = return result; """ -proc SetEq(a, b: int): bool {.compilerproc, noStackFrame.} = +proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = asm """ for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `b`) { if (!`a`[elem]) return false; } return true; """ -proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} = +proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = asm """ for (var elem in `a`) { if (!`b`[elem]) return false; } return true; @@ -194,7 +194,7 @@ proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} = proc SetLt(a, b: int): bool {.compilerproc.} = result = SetLe(a, b) and not SetEq(a, b) -proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} = +proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = asm """ var result = {}; for (var elem in `a`) { @@ -203,7 +203,7 @@ proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} = return result; """ -proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} = +proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = asm """ var result = {}; for (var elem in `a`) { result[elem] = true; } @@ -211,7 +211,7 @@ proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} = return result; """ -proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} = +proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = asm """ var result = {}; for (var elem in `a`) { @@ -220,7 +220,7 @@ proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} = return result; """ -proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} = +proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = asm """ if (`a` == `b`) return 0; if (!`a`) return -1; @@ -234,7 +234,7 @@ proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} = proc cmp(x, y: string): int = return cmpStrings(x, y) -proc eqStrings(a, b: string): bool {.noStackFrame, compilerProc.} = +proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = asm """ if (`a` == `b`) return true; if ((!`a`) || (!`b`)) return false; @@ -300,7 +300,7 @@ type setAttributeNode*: proc (attr: ref TNode) {.nimcall.} when defined(kwin): - proc rawEcho {.compilerproc, nostackframe.} = + proc rawEcho {.compilerproc, asmNoStackFrame.} = asm """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { @@ -312,7 +312,7 @@ when defined(kwin): elif defined(nodejs): proc ewriteln(x: cstring) = log(x) - proc rawEcho {.compilerproc, nostackframe.} = + proc rawEcho {.compilerproc, asmNoStackFrame.} = asm """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { @@ -345,42 +345,42 @@ else: node.appendChild(document.createElement("br")) # Arithmetic: -proc addInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` + `b`; if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); return result; """ -proc subInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` - `b`; if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); return result; """ -proc mulInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` * `b`; if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); return result; """ -proc divInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); return Math.floor(`a` / `b`); """ -proc modInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); return Math.floor(`a` % `b`); """ -proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` + `b`; if (result > 9223372036854775807 @@ -388,7 +388,7 @@ proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} = return result; """ -proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` - `b`; if (result > 9223372036854775807 @@ -396,7 +396,7 @@ proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} = return result; """ -proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` * `b`; if (result > 9223372036854775807 @@ -404,90 +404,89 @@ proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} = return result; """ -proc divInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); return Math.floor(`a` / `b`); """ -proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); return Math.floor(`a` % `b`); """ -proc NegInt(a: int): int {.compilerproc.} = +proc negInt(a: int): int {.compilerproc.} = result = a*(-1) -proc NegInt64(a: int64): int64 {.compilerproc.} = +proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) -proc AbsInt(a: int): int {.compilerproc.} = +proc absInt(a: int): int {.compilerproc.} = result = if a < 0: a*(-1) else: a -proc AbsInt64(a: int64): int64 {.compilerproc.} = +proc absInt64(a: int64): int64 {.compilerproc.} = result = if a < 0: a*(-1) else: a -proc LeU(a, b: int): bool {.compilerproc.} = +proc leU(a, b: int): bool {.compilerproc.} = result = abs(a) <= abs(b) -proc LtU(a, b: int): bool {.compilerproc.} = +proc ltU(a, b: int): bool {.compilerproc.} = result = abs(a) < abs(b) -proc LeU64(a, b: int64): bool {.compilerproc.} = +proc leU64(a, b: int64): bool {.compilerproc.} = result = abs(a) <= abs(b) - -proc LtU64(a, b: int64): bool {.compilerproc.} = +proc ltU64(a, b: int64): bool {.compilerproc.} = result = abs(a) < abs(b) -proc AddU(a, b: int): int {.compilerproc.} = +proc addU(a, b: int): int {.compilerproc.} = result = abs(a) + abs(b) -proc AddU64(a, b: int64): int64 {.compilerproc.} = +proc addU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) + abs(b) -proc SubU(a, b: int): int {.compilerproc.} = +proc subU(a, b: int): int {.compilerproc.} = result = abs(a) - abs(b) -proc SubU64(a, b: int64): int64 {.compilerproc.} = +proc subU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) - abs(b) -proc MulU(a, b: int): int {.compilerproc.} = +proc mulU(a, b: int): int {.compilerproc.} = result = abs(a) * abs(b) -proc MulU64(a, b: int64): int64 {.compilerproc.} = +proc mulU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) * abs(b) -proc DivU(a, b: int): int {.compilerproc.} = +proc divU(a, b: int): int {.compilerproc.} = result = abs(a) div abs(b) -proc DivU64(a, b: int64): int64 {.compilerproc.} = +proc divU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) div abs(b) -proc ModU(a, b: int): int {.compilerproc.} = +proc modU(a, b: int): int {.compilerproc.} = result = abs(a) mod abs(b) -proc ModU64(a, b: int64): int64 {.compilerproc.} = +proc modU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) mod abs(b) -proc Ze(a: int): int {.compilerproc.} = +proc ze*(a: int): int {.compilerproc.} = result = a -proc Ze64(a: int64): int64 {.compilerproc.} = + +proc ze64*(a: int64): int64 {.compilerproc.} = result = a -proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} = +proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} = +proc ToU16(a: int): int16 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} = +proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ - proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b @@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool = tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr} -proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} +proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} -proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = +proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = case n.kind of nkNone: sysAssert(false, "NimCopyAux") of nkSlot: @@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = } """ -proc NimCopy(x: pointer, ti: PNimType): pointer = +proc nimCopy(x: pointer, ti: PNimType): pointer = case ti.kind of tyPtr, tyRef, tyVar, tyNil: if not isFatPointer(ti): @@ -586,7 +585,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = result = nil proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. - noStackFrame, compilerproc.} = + asmNoStackFrame, compilerproc.} = # types are fake asm """ var result = new Array(`len`); @@ -620,7 +619,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = x = x.base return true -proc addChar(x: string, c: char) {.compilerproc, noStackFrame.} = +proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = asm """ `x`[`x`.length-1] = `c`; `x`.push(0); """ diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 5b641185e..74ef9c9ec 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -16,8 +16,12 @@ const type THandle* = int LONG* = int32 + ULONG* = int + PULONG* = ptr int WINBOOL* = int32 DWORD* = int32 + PDWORD* = ptr DWORD + LPINT* = ptr int32 HDC* = THandle HGLRC* = THandle @@ -195,14 +199,14 @@ else: importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} - proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. + proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {. importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} proc removeDirectoryA*(lpPathName: cstring): int32 {. importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} - proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. + proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {. importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} when useWinUnicode: @@ -300,7 +304,7 @@ else: dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} - proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, + proc copyFileA*(lpExistingFileName, lpNewFileName: CString, bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} @@ -632,3 +636,76 @@ when not useWinUnicode: proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, dynlib: "kernel32", importc: "UnmapViewOfFile".} +type + TOVERLAPPED* {.final, pure.} = object + Internal*: DWORD + InternalHigh*: DWORD + Offset*: DWORD + OffsetHigh*: DWORD + hEvent*: THANDLE + + POVERLAPPED* = ptr TOVERLAPPED + + POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD, + para3: POVERLAPPED){.stdcall.} + + TGUID* {.final, pure.} = object + D1*: int32 + D2*: int16 + D3*: int16 + D4*: array [0..7, int8] + +const + ERROR_IO_PENDING* = 997 + +proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE, + CompletionKey: DWORD, + NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall, + dynlib: "kernel32", importc: "CreateIoCompletionPort".} + +proc GetQueuedCompletionStatus*(CompletionPort: THandle, + lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG, + lpOverlapped: ptr POverlapped, + dwMilliseconds: DWORD): WINBOOL{.stdcall, + dynlib: "kernel32", importc: "GetQueuedCompletionStatus".} + +const + IOC_OUT* = 0x40000000 + IOC_IN* = 0x80000000 + IOC_WS2* = 0x08000000 + IOC_INOUT* = IOC_IN or IOC_OUT + +template WSAIORW*(x,y): expr = (IOC_INOUT or x or y) + +const + SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD + SO_UPDATE_ACCEPT_CONTEXT* = 0x700B + +var + WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [ + 0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8]) + WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ + 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) + WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ + 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) + +proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, + cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD, + lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED, + lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint + {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".} + +type + TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object + len*: ULONG + buf*: cstring + +proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, + bytesReceived, flags: PDWORD, lpOverlapped: POverlapped, + completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. + stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".} + +proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, + bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped, + completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. + stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim index c4c6ac071..cd3a765c1 100644 --- a/lib/wrappers/zip/zlib.nim +++ b/lib/wrappers/zip/zlib.nim @@ -134,6 +134,7 @@ proc gzerror*(thefile: gzFile, errnum: var int32): pbytef{.cdecl, dynlib: libz, importc: "gzerror".} proc adler32*(adler: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz, importc: "adler32".} + ## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling. proc crc32*(crc: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz, importc: "crc32".} proc deflateInitu*(strm: var TZStream, level: int32, version: cstring, |