-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Hi, I needed a buffered writer in Eio, and I used Faraday as the basis of that (ocaml-multicore/eio#235). While reviewing the code, I fixed a few things that might want fixing here too.
Overflow
There is a comment in shift_flushes saying that it avoids overflow problems by subtracting both sides:
Lines 392 to 410 in 3ea082b
| | (threshold, f) as flush -> | |
| (* Edited notes from @dinosaure: | |
| * | |
| * The quantities [t.bytes_written] and [threshold] are always going to be | |
| * positive integers. Therefore, we can treat them as unsinged integers for | |
| * the purposes of comparision. Doing so allows us to handle overflows in | |
| * either quantity as long as they're both within one overflow of each other. | |
| * We can accomplish this by subracting [min_int] from both quantities before | |
| * comparision. This shift a quantity that has not overflowed into the | |
| * negative integer range while shifting a quantity that has overflow into | |
| * the positive integer range. | |
| * | |
| * This effectively restablishes the relative difference when an overflow | |
| * has occurred, and otherwise just compares numbers that haven't | |
| * overflowed as similarly, just shifted down a bit. | |
| *) | |
| if t.bytes_written - min_int >= threshold - min_int | |
| then begin f reason; shift_flushes t ~reason end | |
| else Flushes.enqueue_front flush t.flushed |
This doesn't work; it just shifts the problem elsewhere (consider bytes_written = -2 and threshold = 8, for example). I believe the test should be t.bytes_written - threshold >= 0. /cc @dinosaure
Safety
write_string and write_bytes both perform unsafe blits using offsets provided by the user:
Lines 250 to 258 in 3ea082b
| let write_string = | |
| let length = String.length in | |
| let blit = Bigstringaf.unsafe_blit_from_string in | |
| fun t ?off ?len a -> write_gen t ~length ~blit ?off ?len a | |
| let write_bytes = | |
| let length = Bytes.length in | |
| let blit = Bigstringaf.unsafe_blit_from_bytes in | |
| fun t ?off ?len a -> write_gen t ~length ~blit ?off ?len a |
Serialize loop
If the user's writev function returns Closed then it can't consume anything and just calls it again, forever:
Lines 436 to 444 in 3ea082b
| let rec serialize t writev = | |
| match operation t with | |
| | `Writev iovecs -> | |
| begin match writev iovecs with | |
| | `Ok n -> shift t n; if not (Buffers.is_empty t.scheduled) then yield t | |
| | `Closed -> close t | |
| end; | |
| serialize t writev | |
| | (`Close|`Yield) as next -> next |
License
BTW, Faraday uses the BSD-3-clause license, whereas Eio uses ISC. I included your headers and license in Eio, but if you're willing to let us use it as ISC it would simplify things slightly.