• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.internal;
18 
19 import static com.google.common.base.Charsets.UTF_8;
20 
21 import com.google.common.base.Preconditions;
22 import io.grpc.KnownLength;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.nio.ByteBuffer;
27 import java.nio.charset.Charset;
28 
29 /**
30  * Utility methods for creating {@link ReadableBuffer} instances.
31  */
32 public final class ReadableBuffers {
33   private static final ReadableBuffer EMPTY_BUFFER = new ByteArrayWrapper(new byte[0]);
34 
35   /**
36    * Returns an empty {@link ReadableBuffer} instance.
37    */
empty()38   public static ReadableBuffer empty() {
39     return EMPTY_BUFFER;
40   }
41 
42   /**
43    * Shortcut for {@code wrap(bytes, 0, bytes.length}.
44    */
wrap(byte[] bytes)45   public static ReadableBuffer wrap(byte[] bytes) {
46     return new ByteArrayWrapper(bytes, 0, bytes.length);
47   }
48 
49   /**
50    * Creates a new {@link ReadableBuffer} that is backed by the given byte array.
51    *
52    * @param bytes the byte array being wrapped.
53    * @param offset the starting offset for the buffer within the byte array.
54    * @param length the length of the buffer from the {@code offset} index.
55    */
wrap(byte[] bytes, int offset, int length)56   public static ReadableBuffer wrap(byte[] bytes, int offset, int length) {
57     return new ByteArrayWrapper(bytes, offset, length);
58   }
59 
60   /**
61    * Creates a new {@link ReadableBuffer} that is backed by the given {@link ByteBuffer}. Calls to
62    * read from the buffer will increment the position of the {@link ByteBuffer}.
63    */
wrap(ByteBuffer bytes)64   public static ReadableBuffer wrap(ByteBuffer bytes) {
65     return new ByteReadableBufferWrapper(bytes);
66   }
67 
68   /**
69    * Reads an entire {@link ReadableBuffer} to a new array. After calling this method, the buffer
70    * will contain no readable bytes.
71    */
readArray(ReadableBuffer buffer)72   public static byte[] readArray(ReadableBuffer buffer) {
73     Preconditions.checkNotNull(buffer, "buffer");
74     int length = buffer.readableBytes();
75     byte[] bytes = new byte[length];
76     buffer.readBytes(bytes, 0, length);
77     return bytes;
78   }
79 
80   /**
81    * Reads the entire {@link ReadableBuffer} to a new {@link String} with the given charset.
82    */
readAsString(ReadableBuffer buffer, Charset charset)83   public static String readAsString(ReadableBuffer buffer, Charset charset) {
84     Preconditions.checkNotNull(charset, "charset");
85     byte[] bytes = readArray(buffer);
86     return new String(bytes, charset);
87   }
88 
89   /**
90    * Reads the entire {@link ReadableBuffer} to a new {@link String} using UTF-8 decoding.
91    */
readAsStringUtf8(ReadableBuffer buffer)92   public static String readAsStringUtf8(ReadableBuffer buffer) {
93     return readAsString(buffer, UTF_8);
94   }
95 
96   /**
97    * Creates a new {@link InputStream} backed by the given buffer. Any read taken on the stream will
98    * automatically increment the read position of this buffer. Closing the stream, however, does not
99    * affect the original buffer.
100    *
101    * @param buffer the buffer backing the new {@link InputStream}.
102    * @param owner if {@code true}, the returned stream will close the buffer when closed.
103    */
openStream(ReadableBuffer buffer, boolean owner)104   public static InputStream openStream(ReadableBuffer buffer, boolean owner) {
105     return new BufferInputStream(owner ? buffer : ignoreClose(buffer));
106   }
107 
108   /**
109    * Decorates the given {@link ReadableBuffer} to ignore calls to {@link ReadableBuffer#close}.
110    *
111    * @param buffer the buffer to be decorated.
112    * @return a wrapper around {@code buffer} that ignores calls to {@link ReadableBuffer#close}.
113    */
ignoreClose(ReadableBuffer buffer)114   public static ReadableBuffer ignoreClose(ReadableBuffer buffer) {
115     return new ForwardingReadableBuffer(buffer) {
116       @Override
117       public void close() {
118         // Ignore.
119       }
120     };
121   }
122 
123   /**
124    * A {@link ReadableBuffer} that is backed by a byte array.
125    */
126   private static class ByteArrayWrapper extends AbstractReadableBuffer {
127     int offset;
128     final int end;
129     final byte[] bytes;
130 
ByteArrayWrapper(byte[] bytes)131     ByteArrayWrapper(byte[] bytes) {
132       this(bytes, 0, bytes.length);
133     }
134 
ByteArrayWrapper(byte[] bytes, int offset, int length)135     ByteArrayWrapper(byte[] bytes, int offset, int length) {
136       Preconditions.checkArgument(offset >= 0, "offset must be >= 0");
137       Preconditions.checkArgument(length >= 0, "length must be >= 0");
138       Preconditions.checkArgument(offset + length <= bytes.length,
139           "offset + length exceeds array boundary");
140       this.bytes = Preconditions.checkNotNull(bytes, "bytes");
141       this.offset = offset;
142       this.end = offset + length;
143     }
144 
145     @Override
readableBytes()146     public int readableBytes() {
147       return end - offset;
148     }
149 
150     @Override
skipBytes(int length)151     public void skipBytes(int length) {
152       checkReadable(length);
153       offset += length;
154     }
155 
156     @Override
readUnsignedByte()157     public int readUnsignedByte() {
158       checkReadable(1);
159       return bytes[offset++] & 0xFF;
160     }
161 
162     @Override
readBytes(byte[] dest, int destIndex, int length)163     public void readBytes(byte[] dest, int destIndex, int length) {
164       System.arraycopy(bytes, offset, dest, destIndex, length);
165       offset += length;
166     }
167 
168     @Override
readBytes(ByteBuffer dest)169     public void readBytes(ByteBuffer dest) {
170       Preconditions.checkNotNull(dest, "dest");
171       int length = dest.remaining();
172       checkReadable(length);
173       dest.put(bytes, offset, length);
174       offset += length;
175     }
176 
177     @Override
readBytes(OutputStream dest, int length)178     public void readBytes(OutputStream dest, int length) throws IOException {
179       checkReadable(length);
180       dest.write(bytes, offset, length);
181       offset += length;
182     }
183 
184     @Override
readBytes(int length)185     public ByteArrayWrapper readBytes(int length) {
186       checkReadable(length);
187       int originalOffset = offset;
188       offset += length;
189       return new ByteArrayWrapper(bytes, originalOffset, length);
190     }
191 
192     @Override
hasArray()193     public boolean hasArray() {
194       return true;
195     }
196 
197     @Override
array()198     public byte[] array() {
199       return bytes;
200     }
201 
202     @Override
arrayOffset()203     public int arrayOffset() {
204       return offset;
205     }
206   }
207 
208   /**
209    * A {@link ReadableBuffer} that is backed by a {@link ByteBuffer}.
210    */
211   private static class ByteReadableBufferWrapper extends AbstractReadableBuffer {
212     final ByteBuffer bytes;
213 
ByteReadableBufferWrapper(ByteBuffer bytes)214     ByteReadableBufferWrapper(ByteBuffer bytes) {
215       this.bytes = Preconditions.checkNotNull(bytes, "bytes");
216     }
217 
218     @Override
readableBytes()219     public int readableBytes() {
220       return bytes.remaining();
221     }
222 
223     @Override
readUnsignedByte()224     public int readUnsignedByte() {
225       checkReadable(1);
226       return bytes.get() & 0xFF;
227     }
228 
229     @Override
skipBytes(int length)230     public void skipBytes(int length) {
231       checkReadable(length);
232       bytes.position(bytes.position() + length);
233     }
234 
235     @Override
readBytes(byte[] dest, int destOffset, int length)236     public void readBytes(byte[] dest, int destOffset, int length) {
237       checkReadable(length);
238       bytes.get(dest, destOffset, length);
239     }
240 
241     @Override
readBytes(ByteBuffer dest)242     public void readBytes(ByteBuffer dest) {
243       Preconditions.checkNotNull(dest, "dest");
244       int length = dest.remaining();
245       checkReadable(length);
246 
247       // Change the limit so that only length bytes are available.
248       int prevLimit = bytes.limit();
249       bytes.limit(bytes.position() + length);
250 
251       // Write the bytes and restore the original limit.
252       dest.put(bytes);
253       bytes.limit(prevLimit);
254     }
255 
256     @Override
readBytes(OutputStream dest, int length)257     public void readBytes(OutputStream dest, int length) throws IOException {
258       checkReadable(length);
259       if (hasArray()) {
260         dest.write(array(), arrayOffset(), length);
261         bytes.position(bytes.position() + length);
262       } else {
263         // The buffer doesn't support array(). Copy the data to an intermediate buffer.
264         byte[] array = new byte[length];
265         bytes.get(array);
266         dest.write(array);
267       }
268     }
269 
270     @Override
readBytes(int length)271     public ByteReadableBufferWrapper readBytes(int length) {
272       checkReadable(length);
273       ByteBuffer buffer = bytes.duplicate();
274       buffer.limit(bytes.position() + length);
275       bytes.position(bytes.position() + length);
276       return new ByteReadableBufferWrapper(buffer);
277     }
278 
279     @Override
hasArray()280     public boolean hasArray() {
281       return bytes.hasArray();
282     }
283 
284     @Override
array()285     public byte[] array() {
286       return bytes.array();
287     }
288 
289     @Override
arrayOffset()290     public int arrayOffset() {
291       return bytes.arrayOffset() + bytes.position();
292     }
293   }
294 
295   /**
296    * An {@link InputStream} that is backed by a {@link ReadableBuffer}.
297    */
298   private static final class BufferInputStream extends InputStream implements KnownLength {
299     final ReadableBuffer buffer;
300 
BufferInputStream(ReadableBuffer buffer)301     public BufferInputStream(ReadableBuffer buffer) {
302       this.buffer = Preconditions.checkNotNull(buffer, "buffer");
303     }
304 
305     @Override
available()306     public int available() throws IOException {
307       return buffer.readableBytes();
308     }
309 
310     @Override
read()311     public int read() {
312       if (buffer.readableBytes() == 0) {
313         // EOF.
314         return -1;
315       }
316       return buffer.readUnsignedByte();
317     }
318 
319     @Override
read(byte[] dest, int destOffset, int length)320     public int read(byte[] dest, int destOffset, int length) throws IOException {
321       if (buffer.readableBytes() == 0) {
322         // EOF.
323         return -1;
324       }
325 
326       length = Math.min(buffer.readableBytes(), length);
327       buffer.readBytes(dest, destOffset, length);
328       return length;
329     }
330 
331     @Override
close()332     public void close() throws IOException {
333       buffer.close();
334     }
335   }
336 
ReadableBuffers()337   private ReadableBuffers() {}
338 }
339