package org.apache.lucene.util;
import java.io.IOException;
import java.io.OutputStream;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class is used as a wrapper to a byte array, extending
* {@link OutputStream}. Data is written in the given byte[] buffer, until its
* length is insufficient. Than the buffer size is doubled and the data is
* written.
*
* This class is Unsafe as it is using a buffer which potentially can be changed
* from the outside. Moreover, when {@link #toByteArray()} is called, the buffer
* itself is returned, and not a copy.
*
* @lucene.experimental
*/
public class UnsafeByteArrayOutputStream extends OutputStream {
private byte[] buffer;
private int index;
private int startIndex;
/**
* Constructs a new output stream, with a default allocated buffer which can
* later be obtained via {@link #toByteArray()}.
*/
public UnsafeByteArrayOutputStream() {
reInit(new byte[32], 0);
}
/**
* Constructs a new output stream, with a given buffer. Writing will start
* at index 0 as a default.
*
* @param buffer
* some space to which writing will be made
*/
public UnsafeByteArrayOutputStream(byte[] buffer) {
reInit(buffer, 0);
}
/**
* Constructs a new output stream, with a given buffer. Writing will start
* at a given index.
*
* @param buffer
* some space to which writing will be made.
* @param startPos
* an index (inclusive) from white data will be written.
*/
public UnsafeByteArrayOutputStream(byte[] buffer, int startPos) {
reInit(buffer, startPos);
}
private void grow(int newLength) {
// It actually should be: (Java 1.7, when its intrinsic on all machines)
// buffer = Arrays.copyOf(buffer, newLength);
byte[] newBuffer = new byte[newLength];
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
buffer = newBuffer;
}
/**
* For reuse-ability, this stream object can be re-initialized with another
* given buffer and starting position.
*
* @param buffer some space to which writing will be made.
* @param startPos an index (inclusive) from white data will be written.
*/
public void reInit(byte[] buffer, int startPos) {
if (buffer.length == 0) {
throw new IllegalArgumentException("initial buffer length must be greater than 0.");
}
this.buffer = buffer;
startIndex = startPos;
index = startIndex;
}
/**
* For reuse-ability, this stream object can be re-initialized with another
* given buffer, using 0 as default starting position.
*
* @param buffer some space to which writing will be made.
*/
public void reInit(byte[] buffer) {
reInit(buffer, 0);
}
/**
* writes a given byte(at the form of an int) to the buffer. If the buffer's
* empty space is insufficient, the buffer is doubled.
*
* @param value byte value to be written
*/
@Override
public void write(int value) throws IOException {
if (index >= buffer.length) {
grow(buffer.length << 1);
}
buffer[index++] = (byte) value;
}
/**
* writes a given byte[], with offset and length to the buffer. If the
* buffer's empty space is insufficient, the buffer is doubled until it
* could contain all the data.
*
* @param b
* byte buffer, containing the source data to be written
* @param off
* index from which data from the buffer b should be written
* @param len
* number of bytes that should be written
*/
@Override
public void write(byte[] b, int off, int len) throws IOException {
// If there's not enough space for the data
int targetLength = index + len;
if (targetLength >= buffer.length) {
// Calculating the new required length of the array, keeping the array
// size a power of 2 if it was initialized like that.
int newlen = buffer.length;
while ((newlen <<= 1) < targetLength) {}
grow(newlen);
}
// Now that we have enough spare space, we could copy the rest of the
// data
System.arraycopy(b, off, buffer, index, len);
// Updating the index to next available index.
index += len;
}
/**
* Returns the byte array saved within the buffer AS IS.
*
* @return the actual inner buffer - not a copy of it.
*/
public byte[] toByteArray() {
return buffer;
}
/**
* Returns the number of relevant bytes. This objects makes sure the buffer
* is at least the size of it's data. But it can also be twice as big. The
* user would want to process the relevant bytes only. For that he would
* need the count.
*
* @return number of relevant bytes
*/
public int length() {
return index;
}
/**
* Returns the start position data was written to. This is useful in case you
* used {@link #reInit(byte[], int)} or
* {@link #UnsafeByteArrayOutputStream(byte[], int)} and passed a start
* position which is not 0.
*/
public int getStartPos() {
return startIndex;
}
}