2
2
3
3
import com .google .cloud .ReadChannel ;
4
4
import com .google .common .base .Optional ;
5
+ import com .google .common .io .ByteSink ;
5
6
import com .google .edwmigration .dbsync .common .storage .AbstractRemoteByteSource ;
6
7
import com .google .edwmigration .dbsync .common .storage .Slice ;
7
8
import com .google .cloud .storage .BlobId ;
@@ -26,23 +27,22 @@ public class GcsByteSource extends AbstractRemoteByteSource {
26
27
27
28
private final Storage storage ;
28
29
private final BlobId blobId ;
29
- private InputStream sourceStream ;
30
-
31
- private long currentSourceOffset = 0 ;
30
+ private InputStreamCache inputStreamCache ;
32
31
33
32
private GcsByteSource (Storage storage , BlobId blobId , @ Nullable Slice slice ) {
34
33
super (slice );
35
34
this .storage = storage ;
36
35
this .blobId = blobId ;
36
+ this .inputStreamCache = new InputStreamCache (
37
+ Channels .newInputStream (storage .reader (blobId )), 0 );
37
38
}
38
39
39
40
private GcsByteSource (Storage storage , BlobId blobId , @ Nullable Slice slice ,
40
- InputStream sourceStream , long currentSourceOffset ) {
41
+ InputStreamCache inputStreamCache ) {
41
42
super (slice );
42
43
this .storage = storage ;
43
44
this .blobId = blobId ;
44
- this .sourceStream = sourceStream ;
45
- this .currentSourceOffset = currentSourceOffset ;
45
+ this .inputStreamCache = inputStreamCache ;
46
46
}
47
47
48
48
public GcsByteSource (Storage storage , BlobId blobId ) {
@@ -51,7 +51,7 @@ public GcsByteSource(Storage storage, BlobId blobId) {
51
51
52
52
@ Override
53
53
protected ByteSource slice (Slice slice ) {
54
- return new GcsByteSource (storage , blobId , slice , sourceStream , currentSourceOffset );
54
+ return new GcsByteSource (storage , blobId , slice , inputStreamCache );
55
55
}
56
56
57
57
@ Override
@@ -60,11 +60,9 @@ public InputStream openStream() throws IOException {
60
60
Slice slice = getSlice ();
61
61
if (slice != null ) {
62
62
channel .seek (slice .getOffset ());
63
- channel = channel .limit (slice .getLength ());
63
+ channel = channel .limit (slice .getLength () + slice . getOffset () );
64
64
}
65
- sourceStream = Channels .newInputStream (channel );
66
- currentSourceOffset = slice .getOffset ();
67
- return sourceStream ;
65
+ return Channels .newInputStream (channel );
68
66
}
69
67
70
68
@ Override
@@ -74,22 +72,27 @@ public long copyTo(OutputStream outputStream) throws IOException {
74
72
return super .copyTo (outputStream );
75
73
}
76
74
77
- if (slice .getOffset () < currentSourceOffset ) {
75
+ if (slice .getOffset () < inputStreamCache . getCurrentOffset () ) {
78
76
if (DEBUG ) {
79
77
LOG .info (String .format ("Target offset %d smaller than current offset %d, reopen stream" ,
80
- slice .getOffset (), currentSourceOffset ));
78
+ slice .getOffset (), inputStreamCache . currentOffset ));
81
79
}
82
- sourceStream .close ();
83
- sourceStream = Channels .newInputStream (storage .reader (blobId ));
84
- currentSourceOffset = 0 ;
80
+ inputStreamCache . getSourceStream () .close ();
81
+ inputStreamCache . setSourceStream ( Channels .newInputStream (storage .reader (blobId ) ));
82
+ inputStreamCache . setCurrentOffset ( 0 ) ;
85
83
}
86
84
87
85
// Skip forward to the desired start.
88
- long toSkip = slice .getOffset () - currentSourceOffset ;
86
+ long toSkip = slice .getOffset () - inputStreamCache . getCurrentOffset () ;
89
87
skip (toSkip );
90
88
return copy (slice .getLength (), outputStream );
91
89
}
92
90
91
+ @ Override
92
+ public long copyTo (ByteSink byteSink ) throws IOException {
93
+ return copyTo (byteSink .openBufferedStream ());
94
+ }
95
+
93
96
@ Override
94
97
protected MoreObjects .ToStringHelper toStringHelper (ToStringHelper helper ) {
95
98
return super .toStringHelper (helper )
@@ -104,12 +107,12 @@ public Optional<Long> sizeIfKnown() {
104
107
private void skip (long length ) throws IOException {
105
108
long remaining = length ;
106
109
while (remaining > 0 ) {
107
- long skipped = sourceStream .skip (remaining );
108
- if (skipped < remaining ) {
110
+ long skipped = inputStreamCache . getSourceStream () .skip (remaining );
111
+ if (skipped == 0 ) {
109
112
throw new EOFException ("Unexpected end of stream while skipping." );
110
113
}
111
114
remaining -= skipped ;
112
- currentSourceOffset += skipped ;
115
+ inputStreamCache . setCurrentOffset ( inputStreamCache . getCurrentOffset () + skipped ) ;
113
116
}
114
117
}
115
118
@@ -119,17 +122,49 @@ private long copy(long copyLength, OutputStream out) throws IOException {
119
122
long remaining = copyLength ;
120
123
while (remaining > 0 ) {
121
124
int bytesToRead = (int ) Math .min (buffer .length , remaining );
122
- int read = sourceStream .read (buffer , 0 , bytesToRead );
125
+ int read = inputStreamCache . getSourceStream () .read (buffer , 0 , bytesToRead );
123
126
if (read == -1 ) {
124
127
throw new EOFException ("Unexpected end of stream while copying " + copyLength
125
128
+ " bytes starting at offset " + getSlice ().getOffset ());
126
129
}
127
130
out .write (buffer , 0 , read );
128
131
remaining -= read ;
129
- currentSourceOffset += read ;
132
+ inputStreamCache . setCurrentOffset ( inputStreamCache . getCurrentOffset () + read ) ;
130
133
}
131
134
132
135
// As we always read until there's no remaining bytes or throw.
133
136
return copyLength ;
134
137
}
138
+
139
+ /**
140
+ * A cache with an {@link InputStream} and a mark of the current offset within the stream. This
141
+ * cache will be shared by this {@link ByteSource} and the copies created by
142
+ * {@link #slice(long, long)} to efficiently implement the {@link #copyTo(OutputStream)} method.
143
+ */
144
+ private static class InputStreamCache {
145
+
146
+ private InputStream sourceStream ;
147
+ private long currentOffset ;
148
+
149
+ private InputStreamCache (InputStream inputStream , long currentOffSet ) {
150
+ this .sourceStream = inputStream ;
151
+ this .currentOffset = currentOffSet ;
152
+ }
153
+
154
+ private InputStream getSourceStream () {
155
+ return sourceStream ;
156
+ }
157
+
158
+ public long getCurrentOffset () {
159
+ return currentOffset ;
160
+ }
161
+
162
+ public void setSourceStream (InputStream inputStream ) {
163
+ this .sourceStream = inputStream ;
164
+ }
165
+
166
+ public void setCurrentOffset (long currentOffset ) {
167
+ this .currentOffset = currentOffset ;
168
+ }
169
+ }
135
170
}
0 commit comments