• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.protobuf.Internal.EMPTY_BYTE_BUFFER;
11 
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.nio.ByteBuffer;
15 import java.util.Iterator;
16 
17 class IterableByteBufferInputStream extends InputStream {
18   /** The {@link Iterator} with type {@link ByteBuffer} of {@code input} */
19   private Iterator<ByteBuffer> iterator;
20   /** The current ByteBuffer; */
21   private ByteBuffer currentByteBuffer;
22   /** The number of ByteBuffers in the input data. */
23   private int dataSize;
24   /**
25    * Current {@code ByteBuffer}'s index
26    *
27    * <p>If index equals dataSize, then all the data in the InputStream has been consumed
28    */
29   private int currentIndex;
30   /** The current position for current ByteBuffer */
31   private int currentByteBufferPos;
32   /** Whether current ByteBuffer has an array */
33   private boolean hasArray;
34   /**
35    * If the current ByteBuffer is unsafe-direct based, currentArray is null; otherwise should be the
36    * array inside ByteBuffer.
37    */
38   private byte[] currentArray;
39   /** Current ByteBuffer's array offset */
40   private int currentArrayOffset;
41   /**
42    * If the current ByteBuffer is unsafe-direct based, currentAddress is the start address of this
43    * ByteBuffer; otherwise should be zero.
44    */
45   private long currentAddress;
46 
IterableByteBufferInputStream(Iterable<ByteBuffer> data)47   IterableByteBufferInputStream(Iterable<ByteBuffer> data) {
48     iterator = data.iterator();
49     dataSize = 0;
50     for (ByteBuffer unused : data) {
51       dataSize++;
52     }
53     currentIndex = -1;
54 
55     if (!getNextByteBuffer()) {
56       currentByteBuffer = EMPTY_BYTE_BUFFER;
57       currentIndex = 0;
58       currentByteBufferPos = 0;
59       currentAddress = 0;
60     }
61   }
62 
getNextByteBuffer()63   private boolean getNextByteBuffer() {
64     currentIndex++;
65     if (!iterator.hasNext()) {
66       return false;
67     }
68     currentByteBuffer = iterator.next();
69     currentByteBufferPos = currentByteBuffer.position();
70     if (currentByteBuffer.hasArray()) {
71       hasArray = true;
72       currentArray = currentByteBuffer.array();
73       currentArrayOffset = currentByteBuffer.arrayOffset();
74     } else {
75       hasArray = false;
76       currentAddress = UnsafeUtil.addressOffset(currentByteBuffer);
77       currentArray = null;
78     }
79     return true;
80   }
81 
updateCurrentByteBufferPos(int numberOfBytesRead)82   private void updateCurrentByteBufferPos(int numberOfBytesRead) {
83     currentByteBufferPos += numberOfBytesRead;
84     if (currentByteBufferPos == currentByteBuffer.limit()) {
85       getNextByteBuffer();
86     }
87   }
88 
89   @Override
read()90   public int read() throws IOException {
91     if (currentIndex == dataSize) {
92       return -1;
93     }
94     if (hasArray) {
95       int result = currentArray[currentByteBufferPos + currentArrayOffset] & 0xFF;
96       updateCurrentByteBufferPos(1);
97       return result;
98     } else {
99       int result = UnsafeUtil.getByte(currentByteBufferPos + currentAddress) & 0xFF;
100       updateCurrentByteBufferPos(1);
101       return result;
102     }
103   }
104 
105   @Override
read(byte[] output, int offset, int length)106   public int read(byte[] output, int offset, int length) throws IOException {
107     if (currentIndex == dataSize) {
108       return -1;
109     }
110     int remaining = currentByteBuffer.limit() - currentByteBufferPos;
111     if (length > remaining) {
112       length = remaining;
113     }
114     if (hasArray) {
115       System.arraycopy(
116           currentArray, currentByteBufferPos + currentArrayOffset, output, offset, length);
117       updateCurrentByteBufferPos(length);
118     } else {
119       int prevPos = currentByteBuffer.position();
120       Java8Compatibility.position(currentByteBuffer, currentByteBufferPos);
121       currentByteBuffer.get(output, offset, length);
122       Java8Compatibility.position(currentByteBuffer, prevPos);
123       updateCurrentByteBufferPos(length);
124     }
125     return length;
126   }
127 }
128