diff --git a/README.md b/README.md index 06f31c5..c0d5714 100644 --- a/README.md +++ b/README.md @@ -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` diff --git a/faststreams/inputs.nim b/faststreams/inputs.nim index a773960..60d4aef 100644 --- a/faststreams/inputs.nim +++ b/faststreams/inputs.nim @@ -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`. diff --git a/tests/test_inputs.nim b/tests/test_inputs.nim index 688678a..54107d8 100644 --- a/tests/test_inputs.nim +++ b/tests/test_inputs.nim @@ -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":