Optional maximum length argument for the buffer function#3
Optional maximum length argument for the buffer function#3jsor merged 1 commit intoreactphp:masterfrom
Conversation
clue
left a comment
There was a problem hiding this comment.
Nice progress, would love to get this feature in! ![]()
README.md
Outdated
| a `Promise` which resolves with the stream data buffer. | ||
| The `buffer(ReadableStreamInterface $stream, int $maxLength = 0)` function can be used to create | ||
| a `Promise` which resolves with the stream data buffer. With an optional maximum length argument | ||
| which defaults to no limit. |
There was a problem hiding this comment.
IMO 0 is a valid limit to ensure no data is sent. I think a default null parameter to mean "no limit" makes more sense here 👍
There was a problem hiding this comment.
Changed it, but using a limit of 0 is kinda pointless 🤐
There was a problem hiding this comment.
I agree that there aren't many use cases for this, but IMO it makes more sense as a extreme value instead of "no value" which would be closer to the other end of the range :-)
README.md
Outdated
| var_dump(json_decode($contents)); | ||
| }, function ($error) { | ||
| // Reaching here when the stream buffer goes above the max size. | ||
| // (In this example that is 1024 bytes.) |
There was a problem hiding this comment.
Note that the promise will also be rejected when a stream error is emitted or when the promise is cancelled.
src/functions.php
Outdated
|
|
||
| $promise = new Promise\Promise(function ($resolve, $reject) use ($stream, &$buffer) { | ||
| $promise = new Promise\Promise(function ($resolve, $reject) use ($stream, $deferred, &$buffer) { | ||
| $deferred->promise()->done($resolve, $reject); |
| $buffer .= $data; | ||
| if ($maxLength > 0 && strlen($buffer) > $maxLength) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); | ||
| } |
There was a problem hiding this comment.
Should probably also stop buffering in the background?
There was a problem hiding this comment.
Actually when the promise is rejected it is automatically removing the buffer listener
|
@clue updated the PR, will squash it once all feedback has been worked in to it and both of you approved the PR 👍 |
| if ($maxLength !== null && strlen($buffer) > (int)$maxLength) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Probably micro-optimization, but what about incremental buffer size counting?
$buffer = '';
$bufferLength = 0;
$bufferer = function ($data) use (&$buffer, &$bufferLength, &$bufferer, $deferred, $maxLength) {
$buffer .= $data;
if (null === $maxLength) {
return;
}
$bufferLength += strlen($data);
if ($bufferLength > $maxLength) {
$deferred->reject(new \RuntimeException('Buffer exceeded maximum length'));
}
};There was a problem hiding this comment.
Talking about optimizations, strlen() is actually O(1), so not much would be gained :-) As an alternative, you can use isset($buffer[$maxLength]) to avoid the expensive function call 👍
010e235 to
7f505f3
Compare
src/functions.php
Outdated
| $bufferer = function ($data) use (&$buffer, &$bufferer, $stream, $deferred, $maxLength) { | ||
| $buffer .= $data; | ||
| if ($maxLength !== null && isset($buffer[$maxLength])) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); |
There was a problem hiding this comment.
This doesn't unregister the data callback, so it might result in a double resolution of the deferred, no?
There was a problem hiding this comment.
There is no double resolution in react/promise, first come, first serve. Subsequent resolutions are ignored.
There is room for improvement for this function in general, but that's something for a follow-up PR.
There was a problem hiding this comment.
What @jsor says, but also when rejecting there the error passes through https://github.com/WyriHaximus-labs/promise-stream/blob/7f505f3915e331b90b6e3a6040a33d4104243e15/src/functions.php#L49-L55 which does remove the listener
7f505f3 to
0ab3812
Compare
src/functions.php
Outdated
| * Creates a `Promise` which resolves with the stream data buffer | ||
| * | ||
| * @param ReadableStreamInterface $stream | ||
| * @param int $maxLength |
There was a problem hiding this comment.
@param int|null $maxLength maximum number of bytes to buffer or null=unlimited? See also signature in README.
src/functions.php
Outdated
| $bufferer = function ($data) use (&$buffer, $deferred, $maxLength) { | ||
| $buffer .= $data; | ||
| if ($maxLength !== null && isset($buffer[$maxLength])) { | ||
| $deferred->reject(new \RuntimeException('Buffer exceeded maximum length')); |
There was a problem hiding this comment.
I would suggest using an OverflowException here and explicitly document this in the README. This way, the user can easily discern normal stream errors from buffer overflow errors 👍
There was a problem hiding this comment.
Same as my comment here reactphp/http#232 (comment), OverflowException would be nice.
tests/BufferTest.php
Outdated
|
|
||
| public function testMaximumSize() | ||
| { | ||
| $this->setExpectedException('\RuntimeException', 'Buffer exceeded maximum length'); |
There was a problem hiding this comment.
Should be moved down to the Block\await()?
8784edc to
5835e5b
Compare
5835e5b to
2744beb
Compare
|
Can this PR be readied for merge + release as it blocks HTTP Server? |
| $bufferer = function ($data) use (&$buffer, $deferred, $maxLength) { | ||
| $buffer .= $data; | ||
| if ($maxLength !== null && isset($buffer[$maxLength])) { | ||
| $deferred->reject(new \OverflowException('Buffer exceeded maximum length')); |
There was a problem hiding this comment.
LGTM, but should be added to documentation 👍
clue
left a comment
There was a problem hiding this comment.
Nice, squashed changes, now let's get this in! ![]()
|
@clue awesome! Thanks for the squashing 👍 |
As discussed on hangouts added an optional maximum length argument to the buffer function.