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.http.nio.util;
29  
30  import java.nio.ByteBuffer;
31  
32  import org.apache.http.annotation.NotThreadSafe;
33  import org.apache.http.io.BufferInfo;
34  
35  /**
36   * A buffer that expand its capacity on demand using {@link ByteBufferAllocator}
37   * interface. Internally, this class is backed by an instance of
38   * {@link ByteBuffer}.
39   * <p>
40   * This class is not thread safe.
41   *
42   * @since 4.0
43   */
44  @SuppressWarnings("deprecation")
45  @NotThreadSafe
46  public class ExpandableBuffer implements BufferInfo, org.apache.http.nio.util.BufferInfo {
47  
48      public final static int INPUT_MODE = 0;
49      public final static int OUTPUT_MODE = 1;
50  
51      private final ByteBufferAllocator allocator;
52  
53      private int mode;
54      protected ByteBuffer buffer = null;
55  
56      /**
57       * Allocates buffer of the given size using the given allocator.
58       *
59       * @param buffersize the buffer size.
60       * @param allocator allocator to be used to allocate {@link ByteBuffer}s.
61       */
62      public ExpandableBuffer(int buffersize, final ByteBufferAllocator allocator) {
63          super();
64          if (allocator == null) {
65              throw new IllegalArgumentException("ByteBuffer allocator may not be null");
66          }
67          this.allocator = allocator;
68          this.buffer = allocator.allocate(buffersize);
69          this.mode = INPUT_MODE;
70      }
71  
72      /**
73       * Returns the current mode:
74       * <p>
75       * {@link #INPUT_MODE}: the buffer is in the input mode.
76       * <p>
77       * {@link #OUTPUT_MODE}: the buffer is in the output mode.
78       *
79       * @return current input/output mode.
80       */
81      protected int getMode() {
82          return this.mode;
83      }
84  
85      /**
86       * Sets output mode. The buffer can now be read from.
87       */
88      protected void setOutputMode() {
89          if (this.mode != OUTPUT_MODE) {
90              this.buffer.flip();
91              this.mode = OUTPUT_MODE;
92          }
93      }
94  
95      /**
96       * Sets input mode. The buffer can now be written into.
97       */
98      protected void setInputMode() {
99          if (this.mode != INPUT_MODE) {
100             if (this.buffer.hasRemaining()) {
101                 this.buffer.compact();
102             } else {
103                 this.buffer.clear();
104             }
105             this.mode = INPUT_MODE;
106         }
107     }
108 
109     private void expandCapacity(int capacity) {
110         ByteBuffer oldbuffer = this.buffer;
111         this.buffer = allocator.allocate(capacity);
112         oldbuffer.flip();
113         this.buffer.put(oldbuffer);
114     }
115 
116     /**
117      * Expands buffer's capacity.
118      */
119     protected void expand() {
120         int newcapacity = (this.buffer.capacity() + 1) << 1;
121         if (newcapacity < 0) {
122             newcapacity = Integer.MAX_VALUE;
123         }
124         expandCapacity(newcapacity);
125     }
126 
127     /**
128      * Ensures the buffer can accommodate the required capacity.
129      *
130      * @param requiredCapacity
131      */
132     protected void ensureCapacity(int requiredCapacity) {
133         if (requiredCapacity > this.buffer.capacity()) {
134             expandCapacity(requiredCapacity);
135         }
136     }
137 
138     /**
139      * Returns the total capacity of this buffer.
140      *
141      * @return total capacity.
142      */
143     public int capacity() {
144         return this.buffer.capacity();
145     }
146 
147     /**
148      * Determines if the buffer contains data.
149      *
150      * @return <code>true</code> if there is data in the buffer,
151      *   <code>false</code> otherwise.
152      */
153     public boolean hasData() {
154         setOutputMode();
155         return this.buffer.hasRemaining();
156     }
157 
158     /**
159      * Returns the length of this buffer.
160      *
161      * @return buffer length.
162      */
163     public int length() {
164         setOutputMode();
165         return this.buffer.remaining();
166     }
167 
168     /**
169      * Returns available capacity of this buffer.
170      *
171      * @return buffer length.
172      */
173     public int available() {
174         setInputMode();
175         return this.buffer.remaining();
176     }
177 
178     /**
179      * Clears buffer.
180      */
181     protected void clear() {
182         this.buffer.clear();
183         this.mode = INPUT_MODE;
184     }
185 
186     @Override
187     public String toString() {
188         StringBuilder sb = new StringBuilder();
189         sb.append("[mode=");
190         int mode = getMode();
191         if (mode == INPUT_MODE) {
192             sb.append("in");
193         } else {
194             sb.append("out");
195         }
196         sb.append(" pos=");
197         sb.append(this.buffer.position());
198         sb.append(" lim=");
199         sb.append(this.buffer.limit());
200         sb.append(" cap=");
201         sb.append(this.buffer.capacity());
202         sb.append("]");
203         return sb.toString();
204     }
205 
206 }