7
7
import java .io .IOException ;
8
8
import java .io .InputStream ;
9
9
import java .io .OutputStream ;
10
+ import java .io .StreamCorruptedException ;
10
11
import java .io .UncheckedIOException ;
11
12
import java .net .URL ;
12
13
import java .nio .ByteBuffer ;
13
14
import java .nio .charset .Charset ;
14
15
import java .nio .charset .StandardCharsets ;
15
16
import java .util .Arrays ;
16
17
import java .util .HashMap ;
18
+ import java .util .Iterator ;
17
19
import java .util .Map ;
20
+ import java .util .NoSuchElementException ;
18
21
import java .util .concurrent .BlockingQueue ;
19
22
import java .util .concurrent .CompletableFuture ;
20
23
import java .util .concurrent .ExecutionException ;
24
27
25
28
import com .clickhouse .data .stream .BlockingInputStream ;
26
29
import com .clickhouse .data .stream .DeferredInputStream ;
27
- import com .clickhouse .data .stream .DelegatedInputStream ;
28
30
import com .clickhouse .data .stream .EmptyInputStream ;
29
31
import com .clickhouse .data .stream .RestrictedInputStream ;
30
32
import com .clickhouse .data .stream .IterableByteArrayInputStream ;
40
42
* creation as well as closing the stream when it reaches end of stream. This
41
43
* class is also responsible for creating various input stream as needed.
42
44
*/
43
- public abstract class ClickHouseInputStream extends InputStream {
45
+ public abstract class ClickHouseInputStream extends InputStream implements Iterable < ClickHouseByteBuffer > {
44
46
protected static final String ERROR_INCOMPLETE_READ = "Reached end of input stream after reading %d of %d bytes" ;
45
47
protected static final String ERROR_NULL_BYTES = "Non-null byte array is required" ;
46
48
protected static final String ERROR_REUSE_BUFFER = "Please pass a different byte array instead of the same internal buffer for reading" ;
47
49
protected static final String ERROR_STREAM_CLOSED = "Input stream has been closed" ;
48
50
49
51
public static final String TYPE_NAME = "InputStream" ;
50
52
53
+ static class ByteBufferIterator implements Iterator <ClickHouseByteBuffer > {
54
+ private final ClickHouseInputStream input ;
55
+
56
+ private ByteBufferIterator (ClickHouseInputStream input ) {
57
+ this .input = input ;
58
+ }
59
+
60
+ @ Override
61
+ public boolean hasNext () {
62
+ try {
63
+ return input .available () > 0 ;
64
+ } catch (IOException e ) {
65
+ throw new UncheckedIOException (e );
66
+ }
67
+ }
68
+
69
+ @ Override
70
+ public ClickHouseByteBuffer next () {
71
+ try {
72
+ ClickHouseByteBuffer buffer = input .nextBuffer ();
73
+ if (buffer .isEmpty () && input .available () < 1 ) {
74
+ throw new NoSuchElementException (
75
+ "No more byte buffer for read as we reached end of the stream" );
76
+ }
77
+ return buffer ;
78
+ } catch (IOException e ) {
79
+ throw new UncheckedIOException (e );
80
+ }
81
+ }
82
+ }
83
+
51
84
/**
52
85
* Wraps the given input stream.
53
86
*
@@ -733,6 +766,22 @@ protected void ensureOpen() throws IOException {
733
766
}
734
767
}
735
768
769
+ /**
770
+ * Gets reference to current byte buffer.
771
+ *
772
+ * @return non-null byte buffer
773
+ */
774
+ protected abstract ClickHouseByteBuffer getBuffer ();
775
+
776
+ /**
777
+ * Gets reference to next byte buffer available for read. An empty byte buffer
778
+ * will be returned ({@code nextBuffer().isEmpty() == true}), when it reaches
779
+ * end of the input stream.
780
+ *
781
+ * @return non-null byte buffer
782
+ */
783
+ protected abstract ClickHouseByteBuffer nextBuffer () throws IOException ;
784
+
736
785
/**
737
786
* Gets underlying file. Same as
738
787
* {@code ClickHouseFile.of(getUnderlyingStream())}.
@@ -827,7 +876,21 @@ public final <T> T setUserData(String key, T value) {
827
876
* @throws IOException when failed to read value from input stream or reached
828
877
* end of the stream
829
878
*/
830
- public abstract long pipe (ClickHouseOutputStream output ) throws IOException ;
879
+ public long pipe (ClickHouseOutputStream output ) throws IOException {
880
+ long count = 0L ;
881
+ if (output == null || output .isClosed ()) {
882
+ return count ;
883
+ }
884
+ ensureOpen ();
885
+
886
+ try (ClickHouseInputStream in = this ) {
887
+ for (ClickHouseByteBuffer buf : in ) {
888
+ count += buf .length ();
889
+ output .writeBuffer (buf );
890
+ }
891
+ }
892
+ return count ;
893
+ }
831
894
832
895
/**
833
896
* Reads an unsigned byte from the input stream. Unlike {@link #read()}, it will
@@ -910,7 +973,7 @@ public byte[] readBytes(int length) throws IOException {
910
973
if (read == -1 ) {
911
974
closeQuietly ();
912
975
throw offset == 0 ? new EOFException ()
913
- : new IOException (ClickHouseUtils .format (ERROR_INCOMPLETE_READ , offset , length ));
976
+ : new StreamCorruptedException (ClickHouseUtils .format (ERROR_INCOMPLETE_READ , offset , length ));
914
977
} else {
915
978
offset += read ;
916
979
}
@@ -1082,13 +1145,10 @@ public final void setCopyToTarget(OutputStream out) throws IOException {
1082
1145
if (this .copyTo != null ) {
1083
1146
this .copyTo .flush ();
1084
1147
} else if (out != null ) {
1085
- // process remaining bytes in current buffer
1086
- readCustom ((b , p , l ) -> {
1087
- if (p < l ) {
1088
- out .write (b , p , l - p );
1089
- }
1090
- return 0 ;
1091
- });
1148
+ ClickHouseByteBuffer buf = getBuffer ();
1149
+ if (!buf .isEmpty ()) {
1150
+ out .write (buf .array (), buf .position (), buf .length ());
1151
+ }
1092
1152
}
1093
1153
this .copyTo = out ;
1094
1154
}
@@ -1113,4 +1173,9 @@ public void close() throws IOException {
1113
1173
ClickHouseDataStreamFactory .handleCustomAction (postCloseAction );
1114
1174
}
1115
1175
}
1176
+
1177
+ @ Override
1178
+ public Iterator <ClickHouseByteBuffer > iterator () {
1179
+ return new ByteBufferIterator (this );
1180
+ }
1116
1181
}
0 commit comments