protobuf.js icon indicating copy to clipboard operation
protobuf.js copied to clipboard

encoding small message has buffer bytelength of 8192

Open redgetan opened this issue 8 years ago • 15 comments

protobuf.js version: 6.8.0

I'm encoding a very small payload and having it sent over socket io as an arraybuffer. However, for some reason, the bytelength of the underlying buffer of my encoded message is 8192, which you can see in this example (https://jsfiddle.net/fLp62wvx/1/) even though the payload is very small. Is this expected behavior?

redgetan avatar Jul 02 '17 16:07 redgetan

This is because the library uses a node-like buffer pool internally. The allocated backing buffer is 8k, but byteOffset and byteLength of the returned Uint8Array point to just the relevant part of it.

dcodeIO avatar Jul 02 '17 18:07 dcodeIO

Is there a "correct" way to handle this in the browser? I've worked around it by copying it to a new Uint8Array, but that seems suboptimal.

amc6 avatar Nov 05 '17 23:11 amc6

When working with unknown-length data, using a buffer pool has its advantages. For instance, node.js also uses one but also provides APIs that accept views (there: Buffers), unlike WebSocket, which doesn't accept views (here: Uint8Arrays) in its .send() method - I still wonder why. Nonetheless, the performance benefit gained by using a buffer pool should still outweigh the final copying step, because the final length is known then.

Of course you can also experiment with this yourself by overriding protobuf.util.pool (see link above for a reference) with your own implementation (i.e. one that doesn't pool at all).

dcodeIO avatar Nov 06 '17 00:11 dcodeIO

Makes sense. Thanks

amc6 avatar Nov 07 '17 19:11 amc6

quick workaround to get ArrayBuffer with correct length Uint8ArrayInstance.slice().buffer

sergerusso avatar Mar 02 '18 11:03 sergerusso

I'm sure that my messages are all below 512 bytes. Does it matters if I change the pool size to 512 ? Should this be a configuration ? May 8192 cause the memery usage grow fast?

chengsu avatar Apr 28 '18 02:04 chengsu

I can confirm that @sergerusso fix with slice() works without issue. No need to call slice().buffer since XHR2 supports data views directly.

This prevent the proto unmarshall errors due to the server receiving a buffer that has a bunch of trailing zeros for padding out the internal pool that protobuf.js uses.

larrymyers avatar May 01 '18 17:05 larrymyers

I'm trying to get this working on IE11 and the Uint8Array object has no method slice(). I know IE11 has no marketshare, but its a requirement of my client.

Anyone have an idea for a workaround?

rynop avatar Feb 28 '19 22:02 rynop

Does IE11 have .subarray? Should be roughly the same.

dcodeIO avatar Feb 28 '19 22:02 dcodeIO

The following polyfill gets around the slice() err on IE11.

// Polyfill for Uint8Array.slice for IE and Safari
if (!Uint8Array.prototype.slice) {
    Object.defineProperty(Uint8Array.prototype, 'slice', {
        value: Array.prototype.slice
    });
}

However, I don't think it actually works. Because when I POST my protobuf to my RPC server (Twirp), my server throws the following 500 err:

failed to parse request proto: proto: can't skip unknown wire type 7. And for some reason I can't get at what exactly was POSTed from IE11 dev console.

Without getting this thread too far off track, @dcodeIO does protobuf.js support IE11? If so I'll keep going down the rabbit hole.

rynop avatar Feb 28 '19 22:02 rynop

I think it should, but can't give any hard guarantees. Such errors usually appear when the data is either truncated or not encoded properly, with the latter usually happening when binary becomes somehow converted to (utf8) text. This must be avoided, i.e. by encoding the data as base64 before POSTing it, and decoding again at the receiver's end.

dcodeIO avatar Feb 28 '19 22:02 dcodeIO

I figured it out. The typedarray-slice is true polyfill implementation of slice for all TypedArray objs (Uint8Array included). @dcodeIO your prompt and thorough responses were much appreciated. Thank you for a great OSS Lib.

rynop avatar Mar 01 '19 16:03 rynop

quick workaround to get ArrayBuffer with correct length Uint8ArrayInstance.slice().buffer

it works. Great

zj1024 avatar Sep 06 '21 17:09 zj1024

Hmmm... shouldn't we expect the message.finish() to return the correctly-sized buffer "by default"? it seems a bit hacky-ish to have to do message.finish().slice().buffer to send the correctly-sized message no?

maelp avatar Nov 22 '23 10:11 maelp

Still an issue in 2024... 🙃

Using slice() worked well for me though.

soritesparadox avatar Jul 14 '24 15:07 soritesparadox