001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.nio.util; 029 030import java.nio.ByteBuffer; 031 032import org.apache.http.io.BufferInfo; 033import org.apache.http.util.Args; 034 035/** 036 * A buffer that expand its capacity on demand using {@link ByteBufferAllocator} 037 * interface. Internally, this class is backed by an instance of 038 * {@link ByteBuffer}. 039 * <p> 040 * This class is not thread safe. 041 * 042 * @since 4.0 043 */ 044@SuppressWarnings("deprecation") 045public class ExpandableBuffer implements BufferInfo, org.apache.http.nio.util.BufferInfo { 046 047 public final static int INPUT_MODE = 0; 048 public final static int OUTPUT_MODE = 1; 049 050 private final ByteBufferAllocator allocator; 051 052 private int mode; 053 protected ByteBuffer buffer = null; 054 055 /** 056 * Allocates buffer of the given size using the given allocator. 057 * 058 * @param buffersize the buffer size. 059 * @param allocator allocator to be used to allocate {@link ByteBuffer}s. 060 */ 061 public ExpandableBuffer(final int buffersize, final ByteBufferAllocator allocator) { 062 super(); 063 Args.notNull(allocator, "ByteBuffer allocator"); 064 this.allocator = allocator; 065 this.buffer = allocator.allocate(buffersize); 066 this.mode = INPUT_MODE; 067 } 068 069 /** 070 * Returns the current mode: 071 * <p> 072 * {@link #INPUT_MODE}: the buffer is in the input mode. 073 * <p> 074 * {@link #OUTPUT_MODE}: the buffer is in the output mode. 075 * 076 * @return current input/output mode. 077 */ 078 protected int getMode() { 079 return this.mode; 080 } 081 082 /** 083 * Sets output mode. The buffer can now be read from. 084 */ 085 protected void setOutputMode() { 086 if (this.mode != OUTPUT_MODE) { 087 this.buffer.flip(); 088 this.mode = OUTPUT_MODE; 089 } 090 } 091 092 /** 093 * Sets input mode. The buffer can now be written into. 094 */ 095 protected void setInputMode() { 096 if (this.mode != INPUT_MODE) { 097 if (this.buffer.hasRemaining()) { 098 this.buffer.compact(); 099 } 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}