1818
1919import com .google .cloud .storage .Crc32cValue .Crc32cLengthKnown ;
2020import com .google .common .annotations .VisibleForTesting ;
21+ import com .google .common .base .MoreObjects ;
2122import com .google .common .base .Preconditions ;
2223import com .google .common .math .IntMath ;
24+ import com .google .common .primitives .Ints ;
2325import com .google .protobuf .ByteString ;
2426import java .math .RoundingMode ;
2527import java .nio .ByteBuffer ;
@@ -97,66 +99,96 @@ ChunkSegment[] segmentBuffers(
9799 // turn this into a single branch, rather than multiple that would need to be checked each
98100 // element of the iteration
99101 if (allowUnalignedBlocks ) {
100- return segmentWithUnaligned (bbs , offset , length );
102+ return segmentWithUnaligned (bbs , offset , length , Long . MAX_VALUE );
101103 } else {
102- return segmentWithoutUnaligned (bbs , offset , length );
104+ return segmentWithoutUnaligned (bbs , offset , length , Long . MAX_VALUE );
103105 }
104106 }
105107
106- private ChunkSegment [] segmentWithUnaligned (ByteBuffer [] bbs , int offset , int length ) {
108+ ChunkSegment [] segmentBuffers (
109+ ByteBuffer [] bbs ,
110+ int offset ,
111+ int length ,
112+ boolean allowUnalignedBlocks ,
113+ long maxBytesToConsume ) {
114+ // turn this into a single branch, rather than multiple that would need to be checked each
115+ // element of the iteration
116+ if (allowUnalignedBlocks ) {
117+ return segmentWithUnaligned (bbs , offset , length , maxBytesToConsume );
118+ } else {
119+ long misaligned = maxBytesToConsume % blockSize ;
120+ long alignedMaxBytesToConsume = maxBytesToConsume - misaligned ;
121+ return segmentWithoutUnaligned (bbs , offset , length , alignedMaxBytesToConsume );
122+ }
123+ }
124+
125+ private ChunkSegment [] segmentWithUnaligned (
126+ ByteBuffer [] bbs , int offset , int length , long maxBytesToConsume ) {
107127 Deque <ChunkSegment > data = new ArrayDeque <>();
108128
129+ long consumed = 0 ;
109130 for (int i = offset ; i < length ; i ++) {
110131 ByteBuffer buffer = bbs [i ];
111132 int remaining ;
112- while ((remaining = buffer .remaining ()) > 0 ) {
113- consumeBytes (data , remaining , buffer );
133+ while ((remaining = buffer .remaining ()) > 0 && consumed < maxBytesToConsume ) {
134+ long remainingConsumable = maxBytesToConsume - consumed ;
135+ int toConsume = remaining ;
136+ if (remainingConsumable < remaining ) {
137+ toConsume = Math .toIntExact (remainingConsumable );
138+ }
139+ long consumeBytes = consumeBytes (data , toConsume , buffer );
140+ consumed += consumeBytes ;
114141 }
115142 }
116143
117144 return data .toArray (new ChunkSegment [0 ]);
118145 }
119146
120- private ChunkSegment [] segmentWithoutUnaligned (ByteBuffer [] bbs , int offset , int length ) {
147+ private ChunkSegment [] segmentWithoutUnaligned (
148+ ByteBuffer [] bbs , int offset , int length , long maxBytesToConsume ) {
121149 Deque <ChunkSegment > data = new ArrayDeque <>();
122150
123- final long totalRemaining = Buffers .totalRemaining (bbs , offset , length );
151+ long buffersTotalRemaining = Buffers .totalRemaining (bbs , offset , length );
152+ final long totalRemaining = Math .min (maxBytesToConsume , buffersTotalRemaining );
124153 long consumedSoFar = 0 ;
125154
126155 int currentBlockPending = blockSize ;
127156
157+ outerloop :
128158 for (int i = offset ; i < length ; i ++) {
129159 ByteBuffer buffer = bbs [i ];
130160 int remaining ;
131161 while ((remaining = buffer .remaining ()) > 0 ) {
132162 long overallRemaining = totalRemaining - consumedSoFar ;
133163 if (overallRemaining < blockSize && currentBlockPending == blockSize ) {
134- break ;
164+ break outerloop ;
135165 }
136166
137167 int numBytesConsumable ;
138- if (remaining >= blockSize ) {
168+ if (remaining >= blockSize && currentBlockPending == blockSize ) {
139169 int blockCount = IntMath .divide (remaining , blockSize , RoundingMode .DOWN );
140170 numBytesConsumable = blockCount * blockSize ;
141- } else if (currentBlockPending < blockSize ) {
142- numBytesConsumable = currentBlockPending ;
143- currentBlockPending = blockSize ;
144171 } else {
145- numBytesConsumable = remaining ;
146- currentBlockPending = currentBlockPending - remaining ;
172+ numBytesConsumable = Math .min (remaining , currentBlockPending );
147173 }
148174 if (numBytesConsumable <= 0 ) {
149- continue ;
175+ break outerloop ;
150176 }
151177
152- consumedSoFar += consumeBytes (data , numBytesConsumable , buffer );
178+ int consumed = consumeBytes (data , numBytesConsumable , buffer );
179+ int currentBlockPendingLessConsumed = currentBlockPending - consumed ;
180+ currentBlockPending = currentBlockPendingLessConsumed % blockSize ;
181+ if (currentBlockPending == 0 ) {
182+ currentBlockPending = blockSize ;
183+ }
184+ consumedSoFar += consumed ;
153185 }
154186 }
155187
156188 return data .toArray (new ChunkSegment [0 ]);
157189 }
158190
159- private long consumeBytes (Deque <ChunkSegment > data , int numBytesConsumable , ByteBuffer buffer ) {
191+ private int consumeBytes (Deque <ChunkSegment > data , int numBytesConsumable , ByteBuffer buffer ) {
160192 // either no chunk or most recent chunk is full, start a new one
161193 ChunkSegment peekLast = data .peekLast ();
162194 if (peekLast == null || peekLast .b .size () == maxSegmentSize ) {
@@ -167,7 +199,8 @@ private long consumeBytes(Deque<ChunkSegment> data, int numBytesConsumable, Byte
167199 } else {
168200 ChunkSegment chunkSoFar = data .pollLast ();
169201 //noinspection ConstantConditions -- covered by peekLast check above
170- int limit = Math .min (numBytesConsumable , maxSegmentSize - chunkSoFar .b .size ());
202+ int limit =
203+ Ints .min (buffer .remaining (), numBytesConsumable , maxSegmentSize - chunkSoFar .b .size ());
171204 ChunkSegment datum = newSegment (buffer , limit );
172205 ChunkSegment plus = chunkSoFar .concat (datum );
173206 data .addLast (plus );
@@ -218,5 +251,14 @@ public Crc32cLengthKnown getCrc32c() {
218251 public boolean isOnlyFullBlocks () {
219252 return onlyFullBlocks ;
220253 }
254+
255+ @ Override
256+ public String toString () {
257+ return MoreObjects .toStringHelper (this )
258+ .add ("crc32c" , crc32c )
259+ .add ("onlyFullBlocks" , onlyFullBlocks )
260+ .add ("b" , b )
261+ .toString ();
262+ }
221263 }
222264}
0 commit comments