manuels / texlive.js

Compiling LaTeX (TeX live) in your browser
http://manuels.github.com/texlive.js/
GNU General Public License v2.0
1.25k stars 139 forks source link

Large PDFs: RangeError on String.fromCharCode.apply... #18

Closed jvilk closed 9 years ago

jvilk commented 10 years ago

If you use String.fromCharCode.apply(String, some_array) with an array that is too large (typically 64K), it will result in a stack overflow in most browsers.

This is a problem here: https://github.com/manuels/texlive.js/blob/master/pre.js#L97

manuels commented 10 years ago

hmm, then we need another (fast) alternative. Do you have an idea what function we could use instead?

jvilk commented 10 years ago

This will be fast enough (and is the solution I used), unless the file produced is multiple megabytes long:

          var res2 = "";
          for (var i = 0; i < res.length; i++) {
            res2 += String.fromCharCode(res[i]);
          }
          res = res2;

If you want faster, then you'll want to call String.fromCharCode.apply(String, some_array) on batches of <64K bytes.

jvilk commented 10 years ago

(Note that the problem is not that you are using String.fromCharCode, it is that you are calling it with 64K+ individual arguments, which all have to fit on the stack.)

xylo04 commented 9 years ago

I'm having this issue with the default demo in index.html: res ends up being a Uint8Array[87281]. The demo works if I severely pare down the content.

manuels commented 9 years ago

@jvilk sorry, I kind of lost track on this bug. @xylo04 Thanks for the pull request, but for performance reasons I'd prefer something like (pseudocode)

var chunk = 32*1024;
for (var i = 0; i < res.length/chunk; i++) {
    res2 += String.fromCharCode(res[i*chunk:(i+1)*chunk]);
}
res2 += String.fromCharCode(res[i*chunk:]);
xylo04 commented 9 years ago

SGTM, I wasn't really happy with my original solution but it got me working. I'll put together another pull request using chunking.

xylo04 commented 9 years ago

FYI, I did try 32K chunks and still ran into RangeErrors; bumped it down to 8K chunks to give it some headroom.

jvilk commented 9 years ago

Yeah, AFAIK the amount of arguments you can use will depend on the current size of the stack, which depends on how many functions you have called prior to calling this method and the size of the arguments to them. 8K seems like a reasonably safe number.

manuels commented 9 years ago

Should be fixed by @xylo04's pull request. Thanks, guys!