17
17
package com .pcloud .networking .protocol ;
18
18
19
19
import com .pcloud .utils .IOUtils ;
20
+ import okio .BufferedSink ;
20
21
import okio .BufferedSource ;
21
22
import okio .Okio ;
22
23
23
24
import java .io .IOException ;
25
+ import java .io .OutputStream ;
24
26
import java .net .ProtocolException ;
25
27
import java .util .ArrayDeque ;
26
28
import java .util .Arrays ;
30
32
import static com .pcloud .utils .IOUtils .closeQuietly ;
31
33
32
34
/**
33
- * Reads bytes from source
35
+ * A reader for pCloud's binary data protocol
34
36
* <p>
35
- * An implementation of {@linkplain ProtocolResponseReader} which is able to read bytes from a {@linkplain BufferedSource}
37
+ * An implementation of {@linkplain ProtocolResponseReader} which can read data
38
+ * encoded in pCloud's binary protocol from a stream of bytes in pull-based fashion
39
+ * with minimal overhead.
36
40
* <p>
37
- * Generally used to read the bytes of a network response into serialized data
41
+ *
42
+ * The implementation can be reused for reading multiple responses from a byte source,
43
+ * as long as they are read completely.
38
44
*
39
45
* @see ProtocolReader
40
46
* @see ProtocolResponseReader
@@ -121,18 +127,17 @@ public long beginResponse() throws IOException {
121
127
lastStringId = 0 ;
122
128
return pullNumber (RESPONSE_LENGTH_BYTESIZE );
123
129
}
124
-
125
- throw new SerializationException ("Trying to start reading a response, which has already been started." );
130
+ throw new IllegalStateException ("An already started response or data after it has not been fully read." );
126
131
}
127
132
128
133
@ Override
129
- public long endResponse () throws IOException {
134
+ public boolean endResponse () throws IOException {
130
135
if (currentScope != SCOPE_RESPONSE ) {
131
136
if (currentScope == SCOPE_NONE ) {
132
- throw new SerializationException ("Trying to end a response " +
137
+ throw new IllegalStateException ("Trying to end a response " +
133
138
"but none is being read, first call beginResponse()" );
134
139
} else {
135
- throw new SerializationException ("Trying to end a response, " +
140
+ throw new IllegalStateException ("Trying to end a response, " +
136
141
"but current scope is " + scopeName (currentScope ));
137
142
}
138
143
}
@@ -146,11 +151,12 @@ public long endResponse() throws IOException {
146
151
popScope ();
147
152
stringCache = null ;
148
153
149
- if (dataLength == UNKNOWN_SIZE ) {
150
- dataLength = 0 ;
154
+ boolean dataAvailable = dataLength != UNKNOWN_SIZE ;
155
+ if (dataAvailable ) {
156
+ pushScope (SCOPE_DATA );
151
157
}
152
158
153
- return dataLength ;
159
+ return dataAvailable ;
154
160
}
155
161
156
162
@ Override
@@ -274,9 +280,30 @@ public boolean hasNext() throws IOException {
274
280
275
281
@ Override
276
282
public long dataContentLength () {
283
+ if (currentScope == SCOPE_NONE ) {
284
+ throw new IllegalStateException ("No response or data after it are being read." );
285
+ }
277
286
return dataLength ;
278
287
}
279
288
289
+ @ Override
290
+ public void readData (OutputStream outputStream ) throws IOException {
291
+ readData (Okio .buffer (Okio .sink (outputStream )));
292
+ }
293
+
294
+ @ Override
295
+ public void readData (BufferedSink sink ) throws IOException {
296
+ if (currentScope != SCOPE_DATA ) {
297
+ throw new IllegalStateException ("Cannot read data," +
298
+ " either the response is not read fully or no data is following." );
299
+ }
300
+
301
+ long length = dataLength ;
302
+ sink .write (bufferedSource , length );
303
+ dataLength = UNKNOWN_SIZE ;
304
+ popScope ();
305
+ }
306
+
280
307
@ Override
281
308
public ProtocolResponseReader newPeekingReader () {
282
309
BytesReader reader = new PeekingByteReader ();
@@ -297,10 +324,9 @@ public ProtocolResponseReader newPeekingReader() {
297
324
@ Override
298
325
public void skipValue () throws IOException {
299
326
300
- if (currentScope == SCOPE_NONE ) {
327
+ if (currentScope == SCOPE_NONE || currentScope == SCOPE_DATA ) {
301
328
throw new IllegalStateException ("Trying to skipValue, but currentScope is " +
302
- currentScope +
303
- ". You must call beginResponse() first." );
329
+ scopeName (currentScope ) + "." );
304
330
}
305
331
306
332
final int type = peekType ();
@@ -509,5 +535,15 @@ private PeekingByteReader() {
509
535
public ProtocolResponseReader newPeekingReader () {
510
536
throw new IllegalStateException ("Cannot call newPeekingReader(), this reader is already non-consuming." );
511
537
}
538
+
539
+ @ Override
540
+ public void readData (OutputStream outputStream ) throws IOException {
541
+ throw new UnsupportedOperationException ("Data cannot be peeked." );
542
+ }
543
+
544
+ @ Override
545
+ public void readData (BufferedSink sink ) throws IOException {
546
+ throw new UnsupportedOperationException ("Data cannot be peeked." );
547
+ }
512
548
}
513
549
}
0 commit comments