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