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,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http.impl.nio;
29  
30  import java.nio.ByteBuffer;
31  
32  /**
33   * A buffer that expand its capacity on demand. Internally, this class is backed
34   * by an instance of {@link ByteBuffer}.
35   * <p>
36   * This class is not thread safe.
37   *
38   * @since 4.0
39   */
40  public class ExpandableBuffer {
41  
42      public final static int INPUT_MODE = 0;
43      public final static int OUTPUT_MODE = 1;
44  
45      private int mode;
46      private ByteBuffer buffer;
47  
48      /**
49       * Allocates buffer of the given size using the given allocator.
50       *
51       * @param buffersize the buffer size.
52       */
53      protected ExpandableBuffer(final int buffersize) {
54          super();
55          this.buffer = ByteBuffer.allocate(buffersize);
56          this.mode = INPUT_MODE;
57      }
58  
59      /**
60       * Returns the current mode:
61       * <p>
62       * {@link #INPUT_MODE}: the buffer is in the input mode.
63       * <p>
64       * {@link #OUTPUT_MODE}: the buffer is in the output mode.
65       *
66       * @return current input/output mode.
67       */
68      public int mode() {
69          return this.mode;
70      }
71  
72      public ByteBuffer buffer() {
73          return this.buffer;
74      }
75  
76      /**
77       * Sets output mode. The buffer can now be read from.
78       */
79      protected void setOutputMode() {
80          if (this.mode != OUTPUT_MODE) {
81              this.buffer.flip();
82              this.mode = OUTPUT_MODE;
83          }
84      }
85  
86      /**
87       * Sets input mode. The buffer can now be written into.
88       */
89      protected void setInputMode() {
90          if (this.mode != INPUT_MODE) {
91              if (this.buffer.hasRemaining()) {
92                  this.buffer.compact();
93              } else {
94                  this.buffer.clear();
95              }
96              this.mode = INPUT_MODE;
97          }
98      }
99  
100     private void expandCapacity(final int capacity) {
101         final ByteBuffer oldbuffer = this.buffer;
102         this.buffer = ByteBuffer.allocate(capacity);
103         oldbuffer.flip();
104         this.buffer.put(oldbuffer);
105     }
106 
107     /**
108      * Expands buffer's capacity.
109      */
110     protected void expand() {
111         int newcapacity = (this.buffer.capacity() + 1) << 1;
112         if (newcapacity < 0) {
113             newcapacity = Integer.MAX_VALUE;
114         }
115         expandCapacity(newcapacity);
116     }
117 
118     /**
119      * Ensures the buffer can accommodate the required capacity.
120      */
121     protected void ensureCapacity(final int requiredCapacity) {
122         if (requiredCapacity > this.buffer.capacity()) {
123             expandCapacity(requiredCapacity);
124         }
125     }
126 
127     /**
128      * Determines if the buffer contains data.
129      *
130      * @return {@code true} if there is data in the buffer,
131      *   {@code false} otherwise.
132      */
133     public boolean hasData() {
134         setOutputMode();
135         return this.buffer.hasRemaining();
136     }
137 
138     /**
139      * Returns the length of this buffer.
140      *
141      * @return buffer length.
142      */
143     public int length() {
144         setOutputMode();
145         return this.buffer.remaining();
146     }
147 
148     /**
149      * Returns available capacity of this buffer.
150      *
151      * @return buffer length.
152      */
153     public int capacity() {
154         setInputMode();
155         return this.buffer.remaining();
156     }
157 
158     /**
159      * Clears buffer.
160      */
161     protected void clear() {
162         this.buffer.clear();
163         this.mode = INPUT_MODE;
164     }
165 
166     @Override
167     public String toString() {
168         final StringBuilder sb = new StringBuilder();
169         sb.append("[mode=");
170         if (mode() == INPUT_MODE) {
171             sb.append("in");
172         } else {
173             sb.append("out");
174         }
175         sb.append(" pos=");
176         sb.append(this.buffer.position());
177         sb.append(" lim=");
178         sb.append(this.buffer.limit());
179         sb.append(" cap=");
180         sb.append(this.buffer.capacity());
181         sb.append("]");
182         return sb.toString();
183     }
184 
185 }