1616
1717package com .google .cloud .storage ;
1818
19- import static com .google .common .base .Preconditions .checkArgument ;
20-
2119import com .google .api .client .http .HttpStatusCodes ;
2220import com .google .api .core .ApiFuture ;
2321import com .google .api .core .SettableApiFuture ;
2422import com .google .api .gax .rpc .ServerStream ;
2523import com .google .api .gax .rpc .ServerStreamingCallable ;
2624import com .google .cloud .storage .Crc32cValue .Crc32cLengthKnown ;
2725import com .google .cloud .storage .UnbufferedReadableByteChannelSession .UnbufferedReadableByteChannel ;
26+ import com .google .protobuf .ByteString ;
2827import com .google .storage .v2 .ChecksummedData ;
2928import com .google .storage .v2 .Object ;
3029import com .google .storage .v2 .ReadObjectRequest ;
@@ -46,25 +45,28 @@ final class GapicUnbufferedReadableByteChannel
4645 private final ReadObjectRequest req ;
4746 private final Hasher hasher ;
4847 private final LazyServerStreamIterator iter ;
48+ private final ResponseContentLifecycleManager rclm ;
4949
5050 private boolean open = true ;
5151 private boolean complete = false ;
5252 private long blobOffset ;
5353
5454 private Object metadata ;
5555
56- private ByteBuffer leftovers ;
56+ private ResponseContentLifecycleHandle leftovers ;
5757
5858 GapicUnbufferedReadableByteChannel (
5959 SettableApiFuture <Object > result ,
6060 ServerStreamingCallable <ReadObjectRequest , ReadObjectResponse > read ,
6161 ReadObjectRequest req ,
62- Hasher hasher ) {
62+ Hasher hasher ,
63+ ResponseContentLifecycleManager rclm ) {
6364 this .result = result ;
6465 this .read = read ;
6566 this .req = req ;
6667 this .hasher = hasher ;
6768 this .blobOffset = req .getReadOffset ();
69+ this .rclm = rclm ;
6870 this .iter = new LazyServerStreamIterator ();
6971 }
7072
@@ -82,15 +84,17 @@ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
8284 ReadCursor c = new ReadCursor (blobOffset , blobOffset + totalBufferCapacity );
8385 while (c .hasRemaining ()) {
8486 if (leftovers != null ) {
85- copy (c , leftovers , dsts , offset , length );
87+ leftovers . copy (c , dsts , offset , length );
8688 if (!leftovers .hasRemaining ()) {
89+ leftovers .close ();
8790 leftovers = null ;
8891 }
8992 continue ;
9093 }
9194
9295 if (iter .hasNext ()) {
9396 ReadObjectResponse resp = iter .next ();
97+ ResponseContentLifecycleHandle handle = rclm .get (resp );
9498 if (resp .hasMetadata ()) {
9599 Object respMetadata = resp .getMetadata ();
96100 if (metadata == null ) {
@@ -107,22 +111,24 @@ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
107111 }
108112 }
109113 ChecksummedData checksummedData = resp .getChecksummedData ();
110- ByteBuffer content = checksummedData .getContent ().asReadOnlyByteBuffer ();
111- // very important to know whether a crc32c value is set. Without checking, protobuf will
114+ ByteString content = checksummedData .getContent ();
115+ int contentSize = content .size ();
116+ // Very important to know whether a crc32c value is set. Without checking, protobuf will
112117 // happily return 0, which is a valid crc32c value.
113118 if (checksummedData .hasCrc32C ()) {
114- Crc32cLengthKnown expected =
115- Crc32cValue .of (checksummedData .getCrc32C (), checksummedData .getContent ().size ());
119+ Crc32cLengthKnown expected = Crc32cValue .of (checksummedData .getCrc32C (), contentSize );
116120 try {
117- hasher .validate (expected , content :: duplicate );
121+ hasher .validate (expected , content . asReadOnlyByteBufferList () );
118122 } catch (IOException e ) {
119123 close ();
120124 throw e ;
121125 }
122126 }
123- copy (c , content , dsts , offset , length );
124- if (content .hasRemaining ()) {
125- leftovers = content ;
127+ handle .copy (c , dsts , offset , length );
128+ if (handle .hasRemaining ()) {
129+ leftovers = handle ;
130+ } else {
131+ handle .close ();
126132 }
127133 } else {
128134 complete = true ;
@@ -144,59 +150,26 @@ public boolean isOpen() {
144150 @ Override
145151 public void close () throws IOException {
146152 open = false ;
147- iter .close ();
153+ try {
154+ if (leftovers != null ) {
155+ leftovers .close ();
156+ }
157+ } finally {
158+ iter .close ();
159+ }
148160 }
149161
150162 ApiFuture <Object > getResult () {
151163 return result ;
152164 }
153165
154- private void copy (ReadCursor c , ByteBuffer content , ByteBuffer [] dsts , int offset , int length ) {
155- long copiedBytes = Buffers .copy (content , dsts , offset , length );
156- c .advance (copiedBytes );
157- }
158-
159166 private IOException closeWithError (String message ) throws IOException {
160167 close ();
161168 StorageException cause =
162169 new StorageException (HttpStatusCodes .STATUS_CODE_PRECONDITION_FAILED , message );
163170 throw new IOException (message , cause );
164171 }
165172
166- /**
167- * Shrink wraps a beginning, offset and limit for tracking state of an individual invocation of
168- * {@link #read}
169- */
170- private static final class ReadCursor {
171- private final long beginning ;
172- private long offset ;
173- private final long limit ;
174-
175- private ReadCursor (long beginning , long limit ) {
176- this .limit = limit ;
177- this .beginning = beginning ;
178- this .offset = beginning ;
179- }
180-
181- public boolean hasRemaining () {
182- return limit - offset > 0 ;
183- }
184-
185- public void advance (long incr ) {
186- checkArgument (incr >= 0 );
187- offset += incr ;
188- }
189-
190- public long read () {
191- return offset - beginning ;
192- }
193-
194- @ Override
195- public String toString () {
196- return String .format ("ReadCursor{begin=%d, offset=%d, limit=%d}" , beginning , offset , limit );
197- }
198- }
199-
200173 private final class LazyServerStreamIterator implements Iterator <ReadObjectResponse >, Closeable {
201174 private ServerStream <ReadObjectResponse > serverStream ;
202175 private Iterator <ReadObjectResponse > responseIterator ;
0 commit comments