View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.util;
21  
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.nio.ByteBuffer;
25  import java.nio.channels.Channels;
26  import java.nio.channels.WritableByteChannel;
27  
28  import org.apache.hadoop.classification.InterfaceAudience;
29  import org.apache.hadoop.classification.InterfaceStability;
30  
31  /**
32   * Not thread safe!
33   */
34  @InterfaceAudience.Public
35  @InterfaceStability.Evolving
36  public class ByteBufferOutputStream extends OutputStream {
37  
38    protected ByteBuffer buf;
39  
40    public ByteBufferOutputStream(int capacity) {
41      this(capacity, false);
42    }
43  
44    public ByteBufferOutputStream(int capacity, boolean useDirectByteBuffer) {
45      if (useDirectByteBuffer) {
46        buf = ByteBuffer.allocateDirect(capacity);
47      } else {
48        buf = ByteBuffer.allocate(capacity);
49      }
50    }
51  
52    public int size() {
53      return buf.position();
54    }
55  
56    /**
57     * This flips the underlying BB so be sure to use it _last_!
58     * @return ByteBuffer
59     */
60    public ByteBuffer getByteBuffer() {
61      buf.flip();
62      return buf;
63    }
64  
65    private void checkSizeAndGrow(int extra) {
66      if ( (buf.position() + extra) > buf.limit()) {
67        // size calculation is complex, because we could overflow negative,
68        // and/or not allocate enough space. this fixes that.
69        int newSize = (int)Math.min((((long)buf.capacity()) * 2),
70            (long)(Integer.MAX_VALUE));
71        newSize = Math.max(newSize, buf.position() + extra);
72  
73        ByteBuffer newBuf = ByteBuffer.allocate(newSize);
74        buf.flip();
75        newBuf.put(buf);
76        buf = newBuf;
77      }
78    }
79  
80    // OutputStream
81    @Override
82    public void write(int b) throws IOException {
83      checkSizeAndGrow(Bytes.SIZEOF_BYTE);
84  
85      buf.put((byte)b);
86    }
87  
88   /**
89    * Writes the complete contents of this byte buffer output stream to
90    * the specified output stream argument.
91    *
92    * @param      out   the output stream to which to write the data.
93    * @exception  IOException  if an I/O error occurs.
94    */
95    public synchronized void writeTo(OutputStream out) throws IOException {
96      WritableByteChannel channel = Channels.newChannel(out);
97      ByteBuffer bb = buf.duplicate();
98      bb.flip();
99      channel.write(bb);
100   }
101 
102   @Override
103   public void write(byte[] b) throws IOException {
104     checkSizeAndGrow(b.length);
105 
106     buf.put(b);
107   }
108 
109   @Override
110   public void write(byte[] b, int off, int len) throws IOException {
111     checkSizeAndGrow(len);
112 
113     buf.put(b, off, len);
114   }
115 
116   @Override
117   public void flush() throws IOException {
118     // noop
119   }
120 
121   @Override
122   public void close() throws IOException {
123     // noop again. heh
124   }
125 
126   public byte[] toByteArray(int offset, int length) {
127     ByteBuffer bb = buf.duplicate();
128     bb.flip();
129 
130     byte[] chunk = new byte[length];
131 
132     bb.position(offset);
133     bb.get(chunk, 0, length);
134     return chunk;
135   }
136 }