Better name and docs for the 'nonBlockingReads' facility

This commit is contained in:
Zahary Karadjov 2020-05-07 12:12:46 +03:00
commit dd63bbfd1c
No known key found for this signature in database
GPG key ID: C8936F8A3073D609
3 changed files with 18 additions and 18 deletions

View file

@ -317,13 +317,13 @@ proc performHandshake(c: Connection): bool {.async.} =
It is assumed that in traditional async code, timeouts will be managed more
explicitly with `sleepAsync` and the `or` operator defined over futures.
#### Non-blocking reads
#### Range-restricted reads
Protocols transmitting serialized payloads often provide information regarding
the size of the payload. When you invoke the deserialization routine, it's
preferable if the provided boundaries are treated like an "end of file" marker
for the deserializer. FastStreams provides an easy way to achieve this without
extra copies and memory allocations through the `nonBlockingReads` facility.
extra copies and memory allocations through the `withReadableRange` facility.
Here is a typical usage:
```nim
@ -333,18 +333,20 @@ proc decodeFrame(s: AsyncInputStream, DecodedType: type): Option[DecodedType] =
let lengthPrefix = toInt32 s.read(4)
if s.readable(lengthPrefix):
s.nonBlockingReads(lengthPrefix):
s.readValue(Json, DecodedType)
s.withReadableRange(lengthPrefix, range):
range.readValue(Json, DecodedType)
```
Please note that the above example uses the [nim-serialization library](https://github.com/status-im/nim-serialization/)
Simply, inside the `nonBlockingReads` block, `s.readable` will return `false`
as soon as the Json parser has consumed the specified number of bytes.
Furthermore, it's guaranteed that no blocking operations are possible and
thus our `AsyncInputStream` will be treated like a normal `InputStream`.
Depending on the complexity of the stream processors, this will often lead
to much more optimal code.
Simply, inside the `withReadableRange` block, `range` becomes a stream for
which `s.readable` will return `false` as soon as the Json parser has consumed
the specified number of bytes.
Furthermore, `withReadableRange` guarantees that all stream operations within
the block will be non-blocking, so it will transform the `AsyncInputStream`
into a regular `InputStream`. Depending on the complexity of the stream
processing functions, this will often lead to significant performance gains.
### `OutputStream` and `AsyncOutputStream`

View file

@ -274,22 +274,20 @@ func getBestContiguousRunway(s: InputStream): Natural =
flipPage s
result = s.span.len
template nonBlockingReads*(sp: InputStream|AsyncInputStream, newName, blk: untyped) =
template withReadableRange*(sp: InputStream|AsyncInputStream,
rangeLen: Natural,
rangeStreamVarName, blk: untyped) =
let s = InputStream sp
const sname = astToStr(sp)
let vtable = s.vtable
s.vtable = nil
try:
let `newName` {.inject.} = s
let `rangeStreamVarName` {.inject.} = s
blk
finally:
s.vtable = vtable
template nonBlockingReads*(sp: InputStream|AsyncInputStream, blk: untyped) =
nonBlockingReads(sp, astToStr(sp), blk)
func totalUnconsumedBytes*(s: InputStream): Natural =
## Returns the number of bytes that are currently sitting within the stream
## buffers and that can be consumed with `read` or `advance`.

View file

@ -139,8 +139,8 @@ procSuite "input stream":
test "non-blocking reads":
let s = fileInput(asciiTableFile, pageSize = 20)
if s.readable:
s.nonBlockingReads:
check s.readAll.len == 20
s.withReadableRange(20, r):
check r.readAll.len == 20
check s.readable
test "simple":