001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.api.util;
021
022
023import org.apache.directory.api.i18n.I18n;
024
025
026/**
027 * A dynamically growing byte[]. 
028 *
029 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
030 */
031public class ExpansibleByteBuffer
032{
033    /** the default initial buffer size */
034    private static final int DEFAULT_INITIAL_SIZE = 10;
035
036    /** the initial size of the buffer in number of bytes: also increment for allocations */
037    private final int initialSize;
038
039    /** the position into the buffer */
040    private int pos = 0;
041
042    /** the bytes of the buffer */
043    private byte[] buf;
044
045
046    /**
047     * Create a default ExpansibleByteBuffer capable of holding 10 bytes
048     */
049    public ExpansibleByteBuffer()
050    {
051        this( DEFAULT_INITIAL_SIZE );
052    }
053
054
055    /**
056     * Creates a ExpansibleByteBuffer which has an initialze size
057     *  
058     * @param initialSize The initial buffer size
059     */
060    public ExpansibleByteBuffer( int initialSize )
061    {
062        if ( initialSize <= 0 )
063        {
064            throw new IllegalArgumentException( I18n.err( I18n.ERR_17042_INITIAL_SIZE_ABOVE_ZERO ) );
065        }
066
067        this.initialSize = initialSize;
068        this.buf = new byte[initialSize];
069    }
070
071
072    /**
073     * Reset the Buffer position to 0. Every new added byte will be put on position 0.
074     * Note that whatever this buffer contained before a call to the clear() method
075     * will not be removed.
076     */
077    public final void clear()
078    {
079        pos = 0;
080    }
081
082
083    /**
084     * @return The position of the last byte in the buffer
085     */
086    public final int position()
087    {
088        return pos;
089    }
090
091
092    /**
093     * Set the position to a specific value
094     * 
095     * @param pos the new position
096     */
097    public final void position( int pos )
098    {
099        this.pos = pos;
100    }
101
102
103    /**
104     * @return The number of bytes that can be added into this buffer
105     */
106    public final int capacity()
107    {
108        return buf.length;
109    }
110
111
112    /**
113     * Returns the byte at a given position. Note that no control is done
114     * on the position validity.
115     * 
116     * @param i The position
117     * @return The byte at the given position in the buffer
118     */
119    public final byte get( int i )
120    {
121        return buf[i];
122    }
123
124
125    /**
126     * Get's the bytes, the backing store for this buffer.  Note
127     * that you need to use the position index to determine where
128     * to stop reading from this buffer.
129     * 
130     * @return The interned Byte[]
131     */
132    public final byte[] buffer()
133    {
134        return buf;
135    }
136
137
138    /**
139     * Get's a copy of the bytes used.
140     * 
141     * @return A copy of the interned Byte[]
142     */
143    public final byte[] copyOfUsedBytes()
144    {
145        byte[] copy = new byte[pos];
146        System.arraycopy( buf, 0, copy, 0, pos );
147        return copy;
148    }
149
150
151    /**
152     * Appends the bytes to this buffer.
153     * 
154     * @param bytes The byte[] to append to the buffer
155     */
156    public final void append( byte[] bytes )
157    {
158        if ( pos + bytes.length > buf.length )
159        {
160            growBuffer( bytes.length );
161        }
162
163        System.arraycopy( bytes, 0, buf, pos, bytes.length );
164        pos += bytes.length;
165    }
166
167
168    /**
169     * Appends a byte to this buffer.
170     * 
171     * @param b The byte to append to the buffer
172     */
173    public final void append( byte b )
174    {
175        if ( pos >= buf.length )
176        {
177            growBuffer();
178        }
179
180        buf[pos] = b;
181        pos++;
182    }
183
184
185    /**
186     * Appends an int to this buffer.  WARNING: the int is truncated to 
187     * a byte value.
188     * 
189     * @param val The integer to append to the buffer
190     */
191    public final void append( int val )
192    {
193        if ( pos >= buf.length )
194        {
195            growBuffer();
196        }
197
198        buf[pos] = ( byte ) val;
199        pos++;
200    }
201
202
203    private void growBuffer( int size )
204    {
205        if ( size > initialSize )
206        {
207            byte[] copy = new byte[buf.length + size];
208            System.arraycopy( buf, 0, copy, 0, pos );
209            this.buf = copy;
210        }
211        else
212        {
213            byte[] copy = new byte[buf.length + initialSize];
214            System.arraycopy( buf, 0, copy, 0, pos );
215            this.buf = copy;
216        }
217    }
218
219
220    private void growBuffer()
221    {
222        byte[] copy = new byte[buf.length + initialSize];
223        System.arraycopy( buf, 0, copy, 0, pos );
224        this.buf = copy;
225    }
226}