1

When using the OkHttp Websocket the listener uses a ByteString to provide the binary payload to the application. I want to feed these bytes into some code which takes a okio.Source (in this particular case, a GzipSource), but I cannot find any good way to do this efficiently.

My current solution looks like this:

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            Buffer gzipBuffer = new Buffer();
            gzipBuffer.write(bytes);

            GzipSource gzipSource = new GzipSource(gzipBuffer);
            ....
        }

The downside with this Buffer.write is that it makes for extra byte copies (and in the Buffer case, segmented ones, even if pooled it's extra overhead). And in this Websocket case, a byte array was just allocated for the ByteString itself (when handed over from the WebSocketReader impl).

My question is: is there any other preferred way to read from a ByteString, via a Source? Since the ByteString is supposed to be immutable and a Source would just hold some read position info, I'm thinking it should be perfectly doable (but not from external code, since I cannot access the byte[]).. So it feels like I'm missing an obvious solution here.. :)

Thanks for any hints or pointers!

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Johan Ström
  • 105
  • 6

1 Answers1

1

The way you've written it is nearly optimal.

Okio is optimized for moving data between transformation layers: compression, framing, threading, etc. Though moving data between layers is super-fast (no copies) there's an up-front cost to getting data into the system initially. That's usually I/O you need to do anyway: loading a file or sending a packet. But in this case you have to copy to get the bytes into the first buffer. This feels inefficient but the upside is your next Source.read() call will be fast so the overall throughout will still be great.

Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • Thanks for replying. You're writing "nearly optimal", is there any room for improvement? Digging a bit further, I see that the the GzipSource relies on the Source (actually BufferedSource) having internal buffer segments. I assume that is what makes this up-front load required, as it would not be able to work on a single long `byte[]` with that implementation? Thus to support such a source it would need to be rewritten, and then of course not compatible with other sources. – Johan Ström Feb 07 '19 at 06:47
  • Yep, that’s exactly it. A single `byte[]` won’t work. – Jesse Wilson Feb 09 '19 at 02:05