package FlexibleEncoding.ORC; /** adapted from ORC @author wangmeng */ import org.apache.hadoop.io.Text; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; 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. */ public class DynamicByteArray implements Serializable{ 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; } } 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); } } /** * Get the size of the buffers. */ public long getSizeInBytes() { return initializedChunks * chunkSize; } }