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     public int capacity() {
141         return this.buffer.capacity();
142     }
143 
144     /**
145      * Determines if the buffer contains data.
146      *
147      * @return <code>true</code> if there is data in the buffer,
148      *   <code>false</code> otherwise.
149      */
150     public boolean hasData() {
151         setOutputMode();
152         return this.buffer.hasRemaining();
153     }
154 
155     /**
156      * Returns the length of this buffer.
157      *
158      * @return buffer length.
159      */
160     public int length() {
161         setOutputMode();
162         return this.buffer.remaining();
163     }
164 
165     /**
166      * Returns available capacity of this buffer.
167      *
168      * @return buffer length.
169      */
170     public int available() {
171         setInputMode();
172         return this.buffer.remaining();
173     }
174 
175     /**
176      * Clears buffer.
177      */
178     protected void clear() {
179         this.buffer.clear();
180         this.mode = INPUT_MODE;
181     }
182 
183     @Override
184     public String toString() {
185         final StringBuilder sb = new StringBuilder();
186         sb.append("[mode=");
187         if (getMode() == INPUT_MODE) {
188             sb.append("in");
189         } else {
190             sb.append("out");
191         }
192         sb.append(" pos=");
193         sb.append(this.buffer.position());
194         sb.append(" lim=");
195         sb.append(this.buffer.limit());
196         sb.append(" cap=");
197         sb.append(this.buffer.capacity());
198         sb.append("]");
199         return sb.toString();
200     }
201 
202 }