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 }