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