1+ package com .nylas ;
2+
3+ import java .io .IOException ;
4+ import java .nio .ByteBuffer ;
5+ import java .nio .channels .ReadableByteChannel ;
6+ import okio .Buffer ;
7+ import okio .Source ;
8+ import okio .Timeout ;
9+
10+ /**
11+ * Creates a Source around a ReadableByteChannel and efficiently reads data using an UnsafeCursor.
12+ *
13+ * <p>This is a basic example showing another use for the UnsafeCursor. Using the
14+ * {@link ByteBuffer#wrap(byte[], int, int) ByteBuffer.wrap()} along with access to Buffer segments,
15+ * a ReadableByteChannel can be given direct access to Buffer data without having to copy the data.
16+ */
17+ final class ByteChannelSource implements Source {
18+ private final ReadableByteChannel channel ;
19+ private final Timeout timeout ;
20+
21+ private final Buffer .UnsafeCursor cursor = new Buffer .UnsafeCursor ();
22+
23+ ByteChannelSource (ReadableByteChannel channel , Timeout timeout ) {
24+ this .channel = channel ;
25+ this .timeout = timeout ;
26+ }
27+
28+ @ Override public long read (Buffer sink , long byteCount ) throws IOException {
29+ if (!channel .isOpen ()) throw new IllegalStateException ("closed" );
30+
31+ try (Buffer .UnsafeCursor ignored = sink .readAndWriteUnsafe (cursor )) {
32+ timeout .throwIfReached ();
33+ long oldSize = sink .size ();
34+ int length = (int ) Math .min (8192 , byteCount );
35+
36+ cursor .expandBuffer (length );
37+ int read = channel .read (ByteBuffer .wrap (cursor .data , cursor .start , length ));
38+ if (read == -1 ) {
39+ cursor .resizeBuffer (oldSize );
40+ return -1 ;
41+ } else {
42+ cursor .resizeBuffer (oldSize + read );
43+ return read ;
44+ }
45+ }
46+ }
47+
48+ @ Override public Timeout timeout () {
49+ return timeout ;
50+ }
51+
52+ @ Override public void close () throws IOException {
53+ channel .close ();
54+ }
55+ }
0 commit comments