This relates to elm/bytes issue 9: Decoder suppresses stack overflow.
Currently, any error thrown in a bytes decoder is caught and makes the decoder return
The reason is that a read (for say a 32-bit integer) can try to read bytes that are out of bounds.
But, exceptions caused by infinite recursion or
Debug.todo are also caught and turned into
When a byte out of bounds is accessed, a RangeError is thrown. The problem is that infinite recursion (i.e. a stack overflow) will also throw a RangeError, which makes it hard to distinguish these two cases.
But ideally, a
Decoder would only result in
Nothing when it does out-of-bounds access.
If that were the case you are immediately alerted of infinite recursion or a
Debug.todo in a decoder, and when the decoder results in
Nothing you know what to look for (too many bytes are read).
I see two options:
- more fine-grained try/catch such that the only caught exceptions must be out-of-bounds access
- bounds checks on the buffer before access
In both cases the rest of the code is not in a
try/catch block so if exceptions occur they will bubble up.
Evan points out in the issue that the bounds check might be expensive. So I made a benchmark (https://jsperf.com/bytes-bounds-check/6):
In Chrome (72.0.3626)
- the bounds check a third slower (when the offset is valid)
try/catchhas no effect on performance when no exception occurs (why is it faster than the baseline though?)
In Firefox (64.0.0)
- bounds check is a quarter slower (when the offset is valid)
try/catchstill has no effect on performance when no exception occurs
- An up-front bounds check is significantly slower for valid offsets
- A try/catch block has no impact for valid offsets
Open questions from my side:
- are the test cases correct, maybe they are optimized in a way that the elm/bytes code could not be?
- I tested this with recent(ish) chrome and firefox on linux. Maybe older/other browsers give different numbers?
Thanks @joakin for pointing out that the Chrome JIT was optimizing in an implausible way. The benchmark was updated to pick a random offset on every iteration. This makes the numbers roughly consistent between browsers.