Home | History | Annotate | Download | only in internal
      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    */
     38   public static ReadableBuffer empty() {
     39     return EMPTY_BUFFER;
     40   }
     41 
     42   /**
     43    * Shortcut for {@code wrap(bytes, 0, bytes.length}.
     44    */
     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    */
     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    */
     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    */
     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    */
     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    */
     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    */
    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    */
    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 
    131     ByteArrayWrapper(byte[] bytes) {
    132       this(bytes, 0, bytes.length);
    133     }
    134 
    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
    146     public int readableBytes() {
    147       return end - offset;
    148     }
    149 
    150     @Override
    151     public void skipBytes(int length) {
    152       checkReadable(length);
    153       offset += length;
    154     }
    155 
    156     @Override
    157     public int readUnsignedByte() {
    158       checkReadable(1);
    159       return bytes[offset++] & 0xFF;
    160     }
    161 
    162     @Override
    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
    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
    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
    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
    193     public boolean hasArray() {
    194       return true;
    195     }
    196 
    197     @Override
    198     public byte[] array() {
    199       return bytes;
    200     }
    201 
    202     @Override
    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 
    214     ByteReadableBufferWrapper(ByteBuffer bytes) {
    215       this.bytes = Preconditions.checkNotNull(bytes, "bytes");
    216     }
    217 
    218     @Override
    219     public int readableBytes() {
    220       return bytes.remaining();
    221     }
    222 
    223     @Override
    224     public int readUnsignedByte() {
    225       checkReadable(1);
    226       return bytes.get() & 0xFF;
    227     }
    228 
    229     @Override
    230     public void skipBytes(int length) {
    231       checkReadable(length);
    232       bytes.position(bytes.position() + length);
    233     }
    234 
    235     @Override
    236     public void readBytes(byte[] dest, int destOffset, int length) {
    237       checkReadable(length);
    238       bytes.get(dest, destOffset, length);
    239     }
    240 
    241     @Override
    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
    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
    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
    280     public boolean hasArray() {
    281       return bytes.hasArray();
    282     }
    283 
    284     @Override
    285     public byte[] array() {
    286       return bytes.array();
    287     }
    288 
    289     @Override
    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 
    301     public BufferInputStream(ReadableBuffer buffer) {
    302       this.buffer = Preconditions.checkNotNull(buffer, "buffer");
    303     }
    304 
    305     @Override
    306     public int available() throws IOException {
    307       return buffer.readableBytes();
    308     }
    309 
    310     @Override
    311     public int read() {
    312       if (buffer.readableBytes() == 0) {
    313         // EOF.
    314         return -1;
    315       }
    316       return buffer.readUnsignedByte();
    317     }
    318 
    319     @Override
    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
    332     public void close() throws IOException {
    333       buffer.close();
    334     }
    335   }
    336 
    337   private ReadableBuffers() {}
    338 }
    339