blob: 1d44f77dbae8021cca62cdf318ba263b5dd08424 [file] [log] [blame]
/**
* 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.
*/
package org.apache.tajo.storage.thirdparty.orc;
import org.apache.hadoop.io.Text;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* A class that is a growable array of bytes. Growth is managed in terms of
* chunks that are allocated when needed.
*/
final class DynamicByteArray {
static final int DEFAULT_CHUNKSIZE = 32 * 1024;
static final int DEFAULT_NUM_CHUNKS = 128;
private final int chunkSize; // our allocation sizes
private byte[][] data; // the real data
private int length; // max set element index +1
private int initializedChunks = 0; // the number of chunks created
public DynamicByteArray() {
this(DEFAULT_NUM_CHUNKS, DEFAULT_CHUNKSIZE);
}
public DynamicByteArray(int numChunks, int chunkSize) {
if (chunkSize == 0) {
throw new IllegalArgumentException("bad chunksize");
}
this.chunkSize = chunkSize;
data = new byte[numChunks][];
}
/**
* Ensure that the given index is valid.
*/
private void grow(int chunkIndex) {
if (chunkIndex >= initializedChunks) {
if (chunkIndex >= data.length) {
int newSize = Math.max(chunkIndex + 1, 2 * data.length);
byte[][] newChunk = new byte[newSize][];
System.arraycopy(data, 0, newChunk, 0, data.length);
data = newChunk;
}
for(int i=initializedChunks; i <= chunkIndex; ++i) {
data[i] = new byte[chunkSize];
}
initializedChunks = chunkIndex + 1;
}
}
public byte get(int index) {
if (index >= length) {
throw new IndexOutOfBoundsException("Index " + index +
" is outside of 0.." +
(length - 1));
}
int i = index / chunkSize;
int j = index % chunkSize;
return data[i][j];
}
public void set(int index, byte value) {
int i = index / chunkSize;
int j = index % chunkSize;
grow(i);
if (index >= length) {
length = index + 1;
}
data[i][j] = value;
}
public int add(byte value) {
int i = length / chunkSize;
int j = length % chunkSize;
grow(i);
data[i][j] = value;
int result = length;
length += 1;
return result;
}
/**
* Copy a slice of a byte array into our buffer.
* @param value the array to copy from
* @param valueOffset the first location to copy from value
* @param valueLength the number of bytes to copy from value
* @return the offset of the start of the value
*/
public int add(byte[] value, int valueOffset, int valueLength) {
int i = length / chunkSize;
int j = length % chunkSize;
grow((length + valueLength) / chunkSize);
int remaining = valueLength;
while (remaining > 0) {
int size = Math.min(remaining, chunkSize - j);
System.arraycopy(value, valueOffset, data[i], j, size);
remaining -= size;
valueOffset += size;
i += 1;
j = 0;
}
int result = length;
length += valueLength;
return result;
}
/**
* Read the entire stream into this array.
* @param in the stream to read from
* @throws IOException
*/
public void readAll(InputStream in) throws IOException {
int currentChunk = length / chunkSize;
int currentOffset = length % chunkSize;
grow(currentChunk);
int currentLength = in.read(data[currentChunk], currentOffset,
chunkSize - currentOffset);
while (currentLength > 0) {
length += currentLength;
currentOffset = length % chunkSize;
if (currentOffset == 0) {
currentChunk = length / chunkSize;
grow(currentChunk);
}
currentLength = in.read(data[currentChunk], currentOffset,
chunkSize - currentOffset);
}
}
/**
* Byte compare a set of bytes against the bytes in this dynamic array.
* @param other source of the other bytes
* @param otherOffset start offset in the other array
* @param otherLength number of bytes in the other array
* @param ourOffset the offset in our array
* @param ourLength the number of bytes in our array
* @return negative for less, 0 for equal, positive for greater
*/
public int compare(byte[] other, int otherOffset, int otherLength,
int ourOffset, int ourLength) {
int currentChunk = ourOffset / chunkSize;
int currentOffset = ourOffset % chunkSize;
int maxLength = Math.min(otherLength, ourLength);
while (maxLength > 0 &&
other[otherOffset] == data[currentChunk][currentOffset]) {
otherOffset += 1;
currentOffset += 1;
if (currentOffset == chunkSize) {
currentChunk += 1;
currentOffset = 0;
}
maxLength -= 1;
}
if (maxLength == 0) {
return otherLength - ourLength;
}
int otherByte = 0xff & other[otherOffset];
int ourByte = 0xff & data[currentChunk][currentOffset];
return otherByte > ourByte ? 1 : -1;
}
/**
* Get the size of the array.
* @return the number of bytes in the array
*/
public int size() {
return length;
}
/**
* Clear the array to its original pristine state.
*/
public void clear() {
length = 0;
for(int i=0; i < data.length; ++i) {
data[i] = null;
}
initializedChunks = 0;
}
/**
* Set a text value from the bytes in this dynamic array.
* @param result the value to set
* @param offset the start of the bytes to copy
* @param length the number of bytes to copy
*/
public void setText(Text result, int offset, int length) {
result.clear();
int currentChunk = offset / chunkSize;
int currentOffset = offset % chunkSize;
int currentLength = Math.min(length, chunkSize - currentOffset);
while (length > 0) {
result.append(data[currentChunk], currentOffset, currentLength);
length -= currentLength;
currentChunk += 1;
currentOffset = 0;
currentLength = Math.min(length, chunkSize - currentOffset);
}
}
/**
* Write out a range of this dynamic array to an output stream.
* @param out the stream to write to
* @param offset the first offset to write
* @param length the number of bytes to write
* @throws IOException
*/
public void write(OutputStream out, int offset,
int length) throws IOException {
int currentChunk = offset / chunkSize;
int currentOffset = offset % chunkSize;
while (length > 0) {
int currentLength = Math.min(length, chunkSize - currentOffset);
out.write(data[currentChunk], currentOffset, currentLength);
length -= currentLength;
currentChunk += 1;
currentOffset = 0;
}
}
@Override
public String toString() {
int i;
StringBuilder sb = new StringBuilder(length * 3);
sb.append('{');
int l = length - 1;
for (i=0; i<l; i++) {
sb.append(Integer.toHexString(get(i)));
sb.append(',');
}
sb.append(get(i));
sb.append('}');
return sb.toString();
}
public void setByteBuffer(ByteBuffer result, int offset, int length) {
result.clear();
int currentChunk = offset / chunkSize;
int currentOffset = offset % chunkSize;
int currentLength = Math.min(length, chunkSize - currentOffset);
while (length > 0) {
result.put(data[currentChunk], currentOffset, currentLength);
length -= currentLength;
currentChunk += 1;
currentOffset = 0;
currentLength = Math.min(length, chunkSize - currentOffset);
}
}
/**
* Gets all the bytes of the array.
*
* @return Bytes of the array
*/
public byte[] get() {
byte[] result = null;
if (length > 0) {
int currentChunk = 0;
int currentOffset = 0;
int currentLength = Math.min(length, chunkSize);
int destOffset = 0;
result = new byte[length];
int totalLength = length;
while (totalLength > 0) {
System.arraycopy(data[currentChunk], currentOffset, result, destOffset, currentLength);
destOffset += currentLength;
totalLength -= currentLength;
currentChunk += 1;
currentOffset = 0;
currentLength = Math.min(totalLength, chunkSize - currentOffset);
}
}
return result;
}
/**
* Get the size of the buffers.
*/
public long getSizeInBytes() {
return initializedChunks * chunkSize;
}
}