fixes #2420; negative indexing for slicing is obsolete (breaking change!)

This commit is contained in:
Araq
2015-03-28 00:15:04 +01:00
parent 46fb0e0bac
commit 2b80d75aa2
8 changed files with 82 additions and 79 deletions

View File

@@ -18,8 +18,8 @@ import sockets, os
## This module implements an asynchronous event loop together with asynchronous
## sockets which use this event loop.
## It is akin to Python's asyncore module. Many modules that use sockets
## have an implementation for this module, those modules should all have a
## ``register`` function which you should use to add the desired objects to a
## have an implementation for this module, those modules should all have a
## ``register`` function which you should use to add the desired objects to a
## dispatcher which you created so
## that you can receive the events associated with that module's object.
##
@@ -27,19 +27,19 @@ import sockets, os
## function in a while loop.
##
## **Note:** Most modules have tasks which need to be ran regularly, this is
## why you should not call ``poll`` with a infinite timeout, or even a
## why you should not call ``poll`` with a infinite timeout, or even a
## very long one. In most cases the default timeout is fine.
##
## **Note:** This module currently only supports select(), this is limited by
## FD_SETSIZE, which is usually 1024. So you may only be able to use 1024
## sockets at a time.
##
##
## Most (if not all) modules that use asyncio provide a userArg which is passed
## on with the events. The type that you set userArg to must be inheriting from
## ``RootObj``!
##
## **Note:** If you want to provide async ability to your module please do not
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
## **Note:** If you want to provide async ability to your module please do not
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
## that in the future this type's fields will not be exported therefore breaking
## your code.
##
@@ -59,11 +59,11 @@ import sockets, os
## socket which will give you the client which is connecting. You should then
## set any events that you want to use on that client and add it to your dispatcher
## using the ``register`` procedure.
##
##
## An example ``handleAccept`` follows:
##
##
## .. code-block:: nim
##
##
## var disp = newDispatcher()
## ...
## proc handleAccept(s: AsyncSocket) =
@@ -74,7 +74,7 @@ import sockets, os
## client.handleRead = ...
## disp.register(client)
## ...
##
##
## For client sockets you should only be interested in the ``handleRead`` and
## ``handleConnect`` events. The former gets called whenever the socket has
## received messages and can be read from and the latter gets called whenever
@@ -83,14 +83,14 @@ import sockets, os
##
## Getting a blocking client from an AsyncSocket
## =============================================
##
##
## If you need a asynchronous server socket but you wish to process the clients
## synchronously then you can use the ``getSocket`` converter to get
## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
## with ``accept`` like so:
##
## .. code-block:: nim
##
##
## proc handleAccept(s: AsyncSocket) =
## var client: Socket
## getSocket(s).accept(client)
@@ -113,11 +113,11 @@ type
handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.}
handleError*: proc (h: RootRef) {.nimcall, gcsafe.}
hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.}
open*: bool
task*: proc (h: RootRef) {.nimcall, gcsafe.}
mode*: FileMode
Delegate* = ref DelegateObj
Dispatcher* = ref DispatcherObj
@@ -144,7 +144,7 @@ type
deleg: Delegate
SocketStatus* = enum
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
SockUDPBound
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
@@ -176,8 +176,8 @@ proc newAsyncSocket(): AsyncSocket =
result.lineBuffer = "".TaintedString
result.sendBuffer = ""
proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP,
proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP,
buffered = true): AsyncSocket =
## Initialises an AsyncSocket object. If a socket cannot be initialised
## EOS is raised.
@@ -236,7 +236,7 @@ proc asyncSockHandleWrite(h: RootRef) =
if AsyncSocket(h).socket.isSSL and not
AsyncSocket(h).socket.gotHandshake:
return
if AsyncSocket(h).info == SockConnecting:
AsyncSocket(h).handleConnect(AsyncSocket(h))
AsyncSocket(h).info = SockConnected
@@ -256,10 +256,10 @@ proc asyncSockHandleWrite(h: RootRef) =
# do nothing instead.
discard
elif bytesSent != sock.sendBuffer.len:
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
sock.sendBuffer = sock.sendBuffer[bytesSent .. ^1]
elif bytesSent == sock.sendBuffer.len:
sock.sendBuffer = ""
if AsyncSocket(h).handleWrite != nil:
AsyncSocket(h).handleWrite(AsyncSocket(h))
except OSError:
@@ -284,7 +284,7 @@ when defined(ssl):
else:
# handshake will set socket's ``sslNoHandshake`` field.
discard AsyncSocket(h).socket.handshake()
proc asyncSockTask(h: RootRef) =
when defined(ssl):
@@ -377,9 +377,9 @@ proc acceptAddr*(server: AsyncSocket, client: var AsyncSocket,
if c == invalidSocket: raiseSocketError(server.socket)
c.setBlocking(false) # TODO: Needs to be tested.
# deleg.open is set in ``toDelegate``.
client.socket = c
client.lineBuffer = "".TaintedString
client.sendBuffer = ""
@@ -393,7 +393,7 @@ proc accept*(server: AsyncSocket, client: var AsyncSocket) =
proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket,
address: string] {.deprecated.} =
## Equivalent to ``sockets.acceptAddr``.
##
##
## **Deprecated since version 0.9.0:** Please use the function above.
var client = newAsyncSocket()
var address: string = ""
@@ -441,17 +441,17 @@ proc isConnected*(s: AsyncSocket): bool =
## Determines whether ``s`` is connected.
return s.info == SockConnected
proc isListening*(s: AsyncSocket): bool =
## Determines whether ``s`` is listening for incoming connections.
## Determines whether ``s`` is listening for incoming connections.
return s.info == SockListening
proc isConnecting*(s: AsyncSocket): bool =
## Determines whether ``s`` is connecting.
## Determines whether ``s`` is connecting.
return s.info == SockConnecting
proc isClosed*(s: AsyncSocket): bool =
## Determines whether ``s`` has been closed.
return s.info == SockClosed
proc isSendDataBuffered*(s: AsyncSocket): bool =
## Determines whether ``s`` has data waiting to be sent, i.e. whether this
## socket's sendBuffer contains data.
## socket's sendBuffer contains data.
return s.sendBuffer.len != 0
proc setHandleWrite*(s: AsyncSocket,
@@ -550,7 +550,7 @@ proc send*(sock: AsyncSocket, data: string) =
sock.sendBuffer.add(data)
sock.deleg.mode = fmReadWrite
elif bytesSent != data.len:
sock.sendBuffer.add(data[bytesSent .. -1])
sock.sendBuffer.add(data[bytesSent .. ^1])
sock.deleg.mode = fmReadWrite
proc timeValFromMilliseconds(timeout = 500): Timeval =
@@ -561,10 +561,10 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
FD_ZERO(fd)
for i in items(s):
for i in items(s):
m = max(m, int(i.fd))
FD_SET(i.fd, fd)
proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
var i = 0
var L = s.len
@@ -576,16 +576,16 @@ proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
inc(i)
setLen(s, L)
proc select(readfds, writefds, exceptfds: var seq[Delegate],
proc select(readfds, writefds, exceptfds: var seq[Delegate],
timeout = 500): int =
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var rd, wr, ex: TFdSet
var m = 0
createFdSet(rd, readfds, m)
createFdSet(wr, writefds, m)
createFdSet(ex, exceptfds, m)
if timeout != -1:
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
else:
@@ -599,7 +599,7 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
## This function checks for events on all the delegates in the `PDispatcher`.
## It then proceeds to call the correct event handler.
##
## This function returns ``True`` if there are file descriptors that are still
## This function returns ``True`` if there are file descriptors that are still
## open, otherwise ``False``. File descriptors that have been
## closed are immediately removed from the dispatcher automatically.
##
@@ -611,7 +611,7 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
var readDg, writeDg, errorDg: seq[Delegate] = @[]
var len = d.delegates.len
var dc = 0
while dc < len:
let deleg = d.delegates[dc]
if (deleg.mode != fmWrite or deleg.mode != fmAppend) and deleg.open:
@@ -625,20 +625,20 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
# File/socket has been closed. Remove it from dispatcher.
d.delegates[dc] = d.delegates[len-1]
dec len
d.delegates.setLen(len)
var hasDataBufferedCount = 0
for d in d.delegates:
if d.hasDataBuffered(d.deleVal):
hasDataBufferedCount.inc()
d.handleRead(d.deleVal)
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
if select(readDg, writeDg, errorDg, timeout) != 0:
for i in 0..len(d.delegates)-1:
if i > len(d.delegates)-1: break # One delegate might've been removed.
@@ -651,7 +651,7 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
deleg.handleWrite(deleg.deleVal)
if deleg notin errorDg:
deleg.handleError(deleg.deleVal)
# Execute tasks
for i in items(d.delegates):
i.task(i.deleVal)
@@ -664,7 +664,7 @@ when isMainModule:
proc testConnect(s: AsyncSocket, no: int) =
echo("Connected! " & $no)
proc testRead(s: AsyncSocket, no: int) =
echo("Reading! " & $no)
var data = ""
@@ -682,31 +682,31 @@ when isMainModule:
var address = ""
s.acceptAddr(client, address)
echo("Accepted ", address)
client.handleRead =
client.handleRead =
proc (s: AsyncSocket) =
testRead(s, 2)
disp.register(client)
proc main =
var d = newDispatcher()
var s = asyncSocket()
s.connect("amber.tenthbit.net", Port(6667))
s.handleConnect =
s.handleConnect =
proc (s: AsyncSocket) =
testConnect(s, 1)
s.handleRead =
s.handleRead =
proc (s: AsyncSocket) =
testRead(s, 1)
d.register(s)
var server = asyncSocket()
server.handleAccept =
proc (s: AsyncSocket) =
proc (s: AsyncSocket) =
testAccept(s, d, 78)
server.bindAddr(Port(5555))
server.listen()
d.register(server)
while d.poll(-1): discard
main()

View File

@@ -2865,7 +2865,7 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
when hostOS != "standalone":
proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
## slice operation for strings.
result = s.substr(x.a-|s, x.b-|s)
result = s.substr(x.a, x.b)
proc `[]=`*(s: var string, x: Slice[int], b: string) =
## slice assignment for strings. If
@@ -2876,8 +2876,8 @@ when hostOS != "standalone":
## var s = "abcdef"
## s[1 .. -2] = "xyz"
## assert s == "axyzf"
var a = x.a-|s
var L = x.b-|s - a + 1
var a = x.a
var L = x.b - a + 1
if L == b.len:
for i in 0 .. <L: s[i+a] = b[i]
else:
@@ -2919,8 +2919,8 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
## slice operation for sequences.
var a = x.a-|s
var L = x.b-|s - a + 1
var a = x.a
var L = x.b - a + 1
newSeq(result, L)
for i in 0.. <L: result[i] = s[i + a]
@@ -2928,8 +2928,8 @@ proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
## slice assignment for sequences. If
## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed.
var a = x.a-|s
var L = x.b-|s - a + 1
var a = x.a
var L = x.b - a + 1
if L == b.len:
for i in 0 .. <L: s[i+a] = b[i]
else: