diff --git a/src/main/java/com/facebook/presto/AbstractSlice.java b/src/main/java/com/facebook/presto/AbstractSlice.java new file mode 100644 index 0000000000000..22266d1f2e3e0 --- /dev/null +++ b/src/main/java/com/facebook/presto/AbstractSlice.java @@ -0,0 +1,110 @@ +package com.facebook.presto; + +import com.google.common.base.Objects; +import com.google.common.primitives.UnsignedBytes; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.charset.Charset; + +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.min; + +public abstract class AbstractSlice + implements Slice +{ + @Override + public byte[] getBytes() + { + return getBytes(0, length()); + } + + @Override + public Slice copySlice() + { + return copySlice(0, length()); + } + + @Override + public SliceInput input() + { + return new SliceInput(this); + } + + @Override + public SliceOutput output() + { + return new BasicSliceOutput(this); + } + + @Override + public ByteBuffer toByteBuffer() + { + return toByteBuffer(0, length()); + } + + @Override + public String toString(Charset charset) + { + return toString(0, length(), charset); + } + + @Override + public String toString(int index, int length, Charset charset) + { + if (length == 0) { + return ""; + } + return Slices.decodeString(toByteBuffer(index, length), charset); + } + + @Override + public String toString() + { + return Objects.toStringHelper(this) + .add("length", length()) + .toString(); + } + + protected void checkIndexLength(int index, int length) + { + checkPositionIndexes(index, index + length, length()); + } + + protected static int channelRead(ReadableByteChannel in, ByteBuffer dst) + throws IOException + { + try { + return in.read(dst); + } + catch (ClosedChannelException e) { + return -1; + } + } + + protected static int channelRead(FileChannel in, ByteBuffer dst, int position) + throws IOException + { + try { + return in.read(dst, position); + } + catch (ClosedChannelException e) { + return -1; + } + } + + protected static int compareByteBuffers(ByteBuffer a, ByteBuffer b) + { + int length = min(a.capacity(), b.capacity()); + for (int i = 0; i < length; i++) { + int v = UnsignedBytes.compare(a.get(i), b.get(i)); + if (v != 0) { + return v; + } + } + return a.capacity() - b.capacity(); + } +} diff --git a/src/main/java/com/facebook/presto/ByteArraySlice.java b/src/main/java/com/facebook/presto/ByteArraySlice.java index 39a6184e3d5ff..2f7a55996ce52 100644 --- a/src/main/java/com/facebook/presto/ByteArraySlice.java +++ b/src/main/java/com/facebook/presto/ByteArraySlice.java @@ -16,29 +16,30 @@ package com.facebook.presto; import com.google.common.base.Preconditions; +import com.google.common.primitives.UnsignedBytes; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.ScatteringByteChannel; -import java.nio.charset.Charset; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; import java.util.Arrays; import static com.facebook.presto.SizeOf.SIZE_OF_BYTE; import static com.facebook.presto.SizeOf.SIZE_OF_INT; import static com.facebook.presto.SizeOf.SIZE_OF_LONG; import static com.facebook.presto.SizeOf.SIZE_OF_SHORT; +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.min; import static java.nio.ByteOrder.LITTLE_ENDIAN; /** * Little Endian slice of a byte array. */ public final class ByteArraySlice - implements Slice + extends AbstractSlice { private final byte[] data; private final int offset; @@ -94,7 +95,7 @@ public int getRawOffset() @Override public byte getByte(int index) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_BYTE, this.length); + checkIndexLength(index, SIZE_OF_BYTE); index += offset; return data[index]; } @@ -108,7 +109,7 @@ public short getUnsignedByte(int index) @Override public short getShort(int index) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_SHORT, this.length); + checkIndexLength(index, SIZE_OF_SHORT); index += offset; return (short) (data[index] & 0xFF | data[index + 1] << 8); } @@ -116,7 +117,7 @@ public short getShort(int index) @Override public int getInt(int index) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_INT, this.length); + checkIndexLength(index, SIZE_OF_INT); index += offset; return (data[index] & 0xff) | (data[index + 1] & 0xff) << 8 | @@ -127,7 +128,7 @@ public int getInt(int index) @Override public long getLong(int index) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_LONG, this.length); + checkIndexLength(index, SIZE_OF_LONG); index += offset; return ((long) data[index] & 0xff) | ((long) data[index + 1] & 0xff) << 8 | @@ -148,22 +149,16 @@ public void getBytes(int index, Slice destination, int destinationIndex, int len @Override public void getBytes(int index, byte[] destination, int destinationIndex, int length) { - Preconditions.checkPositionIndexes(index, index + length, this.length); - Preconditions.checkPositionIndexes(destinationIndex, destinationIndex + length, destination.length); + checkIndexLength(index, length); + checkPositionIndexes(destinationIndex, destinationIndex + length, destination.length); index += offset; System.arraycopy(data, index, destination, destinationIndex, length); } - @Override - public byte[] getBytes() - { - return getBytes(0, length); - } - @Override public byte[] getBytes(int index, int length) { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; if (index == 0) { return Arrays.copyOf(data, length); @@ -178,23 +173,23 @@ public void getBytes(int index, ByteBuffer destination) { Preconditions.checkPositionIndex(index, this.length); index += offset; - destination.put(data, index, Math.min(length, destination.remaining())); + destination.put(data, index, min(length, destination.remaining())); } @Override public void getBytes(int index, OutputStream out, int length) throws IOException { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; out.write(data, index, length); } @Override - public int getBytes(int index, GatheringByteChannel out, int length) + public int getBytes(int index, WritableByteChannel out, int length) throws IOException { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; return out.write(ByteBuffer.wrap(data, index, length)); } @@ -202,7 +197,7 @@ public int getBytes(int index, GatheringByteChannel out, int length) @Override public void setShort(int index, int value) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_SHORT, this.length); + checkIndexLength(index, SIZE_OF_SHORT); index += offset; data[index] = (byte) (value); data[index + 1] = (byte) (value >>> 8); @@ -211,7 +206,7 @@ public void setShort(int index, int value) @Override public void setInt(int index, int value) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_INT, this.length); + checkIndexLength(index, SIZE_OF_INT); index += offset; data[index] = (byte) (value); data[index + 1] = (byte) (value >>> 8); @@ -222,7 +217,7 @@ public void setInt(int index, int value) @Override public void setLong(int index, long value) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_LONG, this.length); + checkIndexLength(index, SIZE_OF_LONG); index += offset; data[index] = (byte) (value); data[index + 1] = (byte) (value >>> 8); @@ -237,7 +232,7 @@ public void setLong(int index, long value) @Override public void setByte(int index, int value) { - Preconditions.checkPositionIndexes(index, index + SIZE_OF_BYTE, this.length); + checkIndexLength(index, SIZE_OF_BYTE); index += offset; data[index] = (byte) value; } @@ -250,16 +245,17 @@ public void setBytes(int index, Slice source, int sourceIndex, int length) setBytes(index, src.data, src.offset + sourceIndex, length); } else { - // TODO: this is slow - setBytes(index, source.getBytes(), sourceIndex, length); + ByteBuffer src = source.toByteBuffer(sourceIndex, length); + ByteBuffer dst = toByteBuffer(index, length); + dst.put(src); } } @Override public void setBytes(int index, byte[] source, int sourceIndex, int length) { - Preconditions.checkPositionIndexes(index, index + length, this.length); - Preconditions.checkPositionIndexes(sourceIndex, sourceIndex + length, source.length); + checkIndexLength(index, length); + checkPositionIndexes(sourceIndex, sourceIndex + length, source.length); index += offset; System.arraycopy(source, sourceIndex, data, index, length); } @@ -267,7 +263,7 @@ public void setBytes(int index, byte[] source, int sourceIndex, int length) @Override public void setBytes(int index, ByteBuffer source) { - Preconditions.checkPositionIndexes(index, index + source.remaining(), this.length); + checkIndexLength(index, source.remaining()); index += offset; source.get(data, index, source.remaining()); } @@ -276,7 +272,7 @@ public void setBytes(int index, ByteBuffer source) public int setBytes(int index, InputStream in, int length) throws IOException { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; int readBytes = 0; do { @@ -297,22 +293,16 @@ public int setBytes(int index, InputStream in, int length) } @Override - public int setBytes(int index, ScatteringByteChannel in, int length) + public int setBytes(int index, ReadableByteChannel in, int length) throws IOException { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; ByteBuffer buf = ByteBuffer.wrap(data, index, length); int readBytes = 0; do { - int localReadBytes; - try { - localReadBytes = in.read(buf); - } - catch (ClosedChannelException e) { - localReadBytes = -1; - } + int localReadBytes = channelRead(in, buf); if (localReadBytes < 0) { if (readBytes == 0) { return -1; @@ -333,19 +323,13 @@ else if (localReadBytes == 0) { public int setBytes(int index, FileChannel in, int position, int length) throws IOException { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; ByteBuffer buf = ByteBuffer.wrap(data, index, length); int readBytes = 0; do { - int localReadBytes; - try { - localReadBytes = in.read(buf, position + readBytes); - } - catch (ClosedChannelException e) { - localReadBytes = -1; - } + int localReadBytes = channelRead(in, buf, position + readBytes); if (localReadBytes < 0) { if (readBytes == 0) { return -1; @@ -362,16 +346,10 @@ else if (localReadBytes == 0) { return readBytes; } - @Override - public Slice copySlice() - { - return copySlice(0, length); - } - @Override public Slice copySlice(int index, int length) { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; byte[] copiedArray = new byte[length]; @@ -379,12 +357,6 @@ public Slice copySlice(int index, int length) return new ByteArraySlice(copiedArray); } - @Override - public Slice slice() - { - return slice(0, length); - } - @Override public ByteArraySlice slice(int index, int length) { @@ -392,79 +364,46 @@ public ByteArraySlice slice(int index, int length) return this; } - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); if (index >= 0 && length == 0) { return Slices.EMPTY_SLICE; } return new ByteArraySlice(data, offset + index, length); } - @Override - public SliceInput input() - { - return new SliceInput(this); - } - - @Override - public SliceOutput output() - { - return new BasicSliceOutput(this); - } - - @Override - public ByteBuffer toByteBuffer() - { - return toByteBuffer(0, length); - } - @Override public ByteBuffer toByteBuffer(int index, int length) { - Preconditions.checkPositionIndexes(index, index + length, this.length); + checkIndexLength(index, length); index += offset; - return ByteBuffer.wrap(data, index, length).order(LITTLE_ENDIAN); - } - - @Override - public String toString(Charset charset) - { - return toString(0, length, charset); - } - - @Override - public String toString(int index, int length, Charset charset) - { - if (length == 0) { - return ""; - } - return Slices.decodeString(toByteBuffer(index, length), charset); + return ByteBuffer.wrap(data, index, length).slice().order(LITTLE_ENDIAN); } @Override public int compareTo(Slice that) { + if (this == that) { + return 0; + } if (that instanceof ByteArraySlice) { return compareTo((ByteArraySlice) that); } - // TODO: this is slow - return compareTo(new ByteArraySlice(that.getBytes())); + return compareByteBuffers(toByteBuffer(), that.toByteBuffer()); } private int compareTo(ByteArraySlice that) { - if (this == that) { - return 0; - } - if (this.data == that.data && length == that.length && offset == that.offset) { + if ((this.data == that.data) && (length == that.length) && (offset == that.offset)) { return 0; } - int minLength = Math.min(this.length, that.length); - for (int i = 0; i < minLength; i++) { - int thisByte = 0xFF & this.data[this.offset + i]; - int thatByte = 0xFF & that.data[that.offset + i]; - if (thisByte != thatByte) { - return (thisByte) - (thatByte); + int length = min(this.length, that.length); + for (int i = 0; i < length; i++) { + byte a = this.data[this.offset + i]; + byte b = that.data[this.offset + i]; + int v = UnsignedBytes.compare(a, b); + if (v != 0) { + return v; } } return this.length - that.length; @@ -484,10 +423,12 @@ public boolean equals(Object o) return equals((ByteArraySlice) o); } - // TODO: this is slow Slice slice = (Slice) o; - return (length == slice.length()) && - Arrays.equals(getBytes(), slice.getBytes()); + if (length == slice.length()) { + return true; + } + + return toByteBuffer().equals(slice.toByteBuffer()); } private boolean equals(ByteArraySlice slice) @@ -528,10 +469,4 @@ public int hashCode() hash = result; return hash; } - - @Override - public String toString() - { - return getClass().getSimpleName() + '(' + "length=" + length() + ')'; - } } diff --git a/src/main/java/com/facebook/presto/ByteBufferSlice.java b/src/main/java/com/facebook/presto/ByteBufferSlice.java new file mode 100644 index 0000000000000..d8a1a7b975041 --- /dev/null +++ b/src/main/java/com/facebook/presto/ByteBufferSlice.java @@ -0,0 +1,328 @@ +package com.facebook.presto; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; + +import static com.facebook.presto.SizeOf.SIZE_OF_BYTE; +import static com.facebook.presto.SizeOf.SIZE_OF_INT; +import static com.facebook.presto.SizeOf.SIZE_OF_LONG; +import static com.facebook.presto.SizeOf.SIZE_OF_SHORT; +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.min; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +/** + * Little Endian slice of a {@link ByteBuffer}. + */ +public final class ByteBufferSlice + extends AbstractSlice +{ + private final ByteBuffer buffer; + + private int hash; + + public ByteBufferSlice(ByteBuffer buffer) + { + this.buffer = buffer.slice().order(LITTLE_ENDIAN); + } + + @Override + public int length() + { + return buffer.capacity(); + } + + @Override + public byte getByte(int index) + { + checkIndexLength(index, SIZE_OF_BYTE); + return buffer.get(index); + } + + @Override + public short getUnsignedByte(int index) + { + return (short) (getByte(index) & 0xFF); + } + + @Override + public short getShort(int index) + { + checkIndexLength(index, SIZE_OF_SHORT); + return buffer.getShort(index); + } + + @Override + public int getInt(int index) + { + checkIndexLength(index, SIZE_OF_INT); + return buffer.getInt(index); + } + + @Override + public long getLong(int index) + { + checkIndexLength(index, SIZE_OF_LONG); + return buffer.getLong(index); + } + + @Override + public void getBytes(int index, Slice destination, int destinationIndex, int length) + { + destination.setBytes(destinationIndex, this, index, length); + } + + @Override + public void getBytes(int index, byte[] destination, int destinationIndex, int length) + { + checkIndexLength(index, length); + checkPositionIndexes(destinationIndex, destinationIndex + length, destination.length); + buffer.get(destination, destinationIndex, length); + } + + @Override + public byte[] getBytes(int index, int length) + { + byte[] bytes = new byte[length]; + toByteBuffer(index, length).get(bytes); + return bytes; + } + + @Override + public void getBytes(int index, ByteBuffer destination) + { + int length = min(length() - index, destination.remaining()); + destination.put(toByteBuffer(index, length)); + } + + @Override + public void getBytes(int index, OutputStream out, int length) + throws IOException + { + checkIndexLength(index, length); + if (buffer.hasArray()) { + out.write(buffer.array(), buffer.arrayOffset() + index, length); + } + else { + writeBuffer(toByteBuffer(index, length), out); + } + } + + private static void writeBuffer(ByteBuffer source, OutputStream out) + throws IOException + { + byte[] bytes = new byte[4096]; + while (source.remaining() > 0) { + int count = min(source.remaining(), bytes.length); + source.get(bytes, 0, count); + out.write(bytes, 0, count); + } + } + + @Override + public int getBytes(int index, WritableByteChannel out, int length) + throws IOException + { + return out.write(toByteBuffer(index, length)); + } + + @Override + public void setShort(int index, int value) + { + buffer.putShort(index, (short) (value & 0xFFFF)); + } + + @Override + public void setInt(int index, int value) + { + buffer.putInt(index, value); + } + + @Override + public void setLong(int index, long value) + { + buffer.putLong(index, value); + } + + @Override + public void setByte(int index, int value) + { + buffer.put(index, (byte) (value & 0xFF)); + } + + @Override + public void setBytes(int index, Slice source, int sourceIndex, int length) + { + ByteBuffer src = source.toByteBuffer(sourceIndex, length); + ByteBuffer dst = toByteBuffer(index, length); + dst.put(src); + } + + @Override + public void setBytes(int index, byte[] source, int sourceIndex, int length) + { + checkPositionIndexes(sourceIndex, sourceIndex + length, source.length); + toByteBuffer(index, length).put(source, sourceIndex, length); + } + + @Override + public void setBytes(int index, ByteBuffer source) + { + checkIndexLength(index, source.remaining()); + ByteBuffer dst = buffer.duplicate(); + dst.position(index); + dst.put(source); + } + + @Override + public int setBytes(int index, InputStream in, int length) + throws IOException + { + checkIndexLength(index, length); + byte[] bytes = new byte[4096]; + ByteBuffer dst = toByteBuffer(index, length); + while (dst.remaining() > 0) { + int n = in.read(bytes); + if (n < 0) { + if (dst.position() == 0) { + return -1; + } + break; + } + dst.put(bytes, 0, n); + } + return dst.position(); + } + + @Override + public int setBytes(int index, ReadableByteChannel in, int length) + throws IOException + { + ByteBuffer dst = toByteBuffer(index, length); + while (dst.remaining() > 0) { + int n = channelRead(in, dst); + if (n < 0) { + if (dst.position() == 0) { + return -1; + } + break; + } + else if (n == 0) { + break; + } + } + return dst.position(); + } + + @Override + public int setBytes(int index, FileChannel in, int position, int length) + throws IOException + { + ByteBuffer dst = toByteBuffer(index, length); + while (dst.remaining() > 0) { + int n = channelRead(in, dst, position + dst.position()); + if (n < 0) { + if (dst.position() == 0) { + return -1; + } + break; + } + else if (n == 0) { + break; + } + } + return dst.position(); + } + + @Override + public Slice copySlice(int index, int length) + { + ByteBuffer src = toByteBuffer(index, length); + ByteBuffer dst = allocateSame(src); + dst.put(src); + return new ByteBufferSlice(dst); + } + + private static ByteBuffer allocateSame(ByteBuffer source) + { + if (source.isDirect()) { + return ByteBuffer.allocateDirect(source.capacity()); + } + return ByteBuffer.allocate(source.capacity()); + } + + @Override + public Slice slice(int index, int length) + { + if ((index == 0) && (length == length())) { + return this; + } + checkIndexLength(index, length); + if (length == 0) { + return Slices.EMPTY_SLICE; + } + return new ByteBufferSlice(toByteBuffer(index, length)); + } + + @Override + public ByteBuffer toByteBuffer(int index, int length) + { + checkIndexLength(index, length); + ByteBuffer out = buffer.duplicate(); + out.position(index); + out.limit(index + length); + return out.slice().order(LITTLE_ENDIAN); + } + + @Override + public int compareTo(Slice that) + { + if (this == that) { + return 0; + } + return compareByteBuffers(toByteBuffer(), that.toByteBuffer()); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (!(o instanceof Slice)) { + return false; + } + + Slice slice = (Slice) o; + if (length() != slice.length()) { + return false; + } + + return buffer.equals(slice.toByteBuffer()); + } + + @Override + public int hashCode() + { + if (hash != 0) { + return hash; + } + + // see definition in interface + int result = 1; + for (int i = 0; i < length(); i++) { + result = (31 * result) + buffer.get(i); + } + if (result == 0) { + result = 1; + } + + hash = result; + return hash; + } +} diff --git a/src/main/java/com/facebook/presto/Slice.java b/src/main/java/com/facebook/presto/Slice.java index 10622037ba6fa..b8148deda83fe 100644 --- a/src/main/java/com/facebook/presto/Slice.java +++ b/src/main/java/com/facebook/presto/Slice.java @@ -5,8 +5,8 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.ScatteringByteChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; /** @@ -144,7 +144,7 @@ void getBytes(int index, OutputStream out, int length) * {@code this.length()} * @throws java.io.IOException if the specified channel threw an exception during I/O */ - int getBytes(int index, GatheringByteChannel out, int length) + int getBytes(int index, WritableByteChannel out, int length) throws IOException; /** @@ -247,7 +247,7 @@ int setBytes(int index, InputStream in, int length) * if {@code index + length} is greater than {@code this.length()} * @throws java.io.IOException if the specified channel threw an exception during I/O */ - int setBytes(int index, ScatteringByteChannel in, int length) + int setBytes(int index, ReadableByteChannel in, int length) throws IOException; int setBytes(int index, FileChannel in, int position, int length) @@ -265,17 +265,9 @@ int setBytes(int index, FileChannel in, int position, int length) */ Slice copySlice(int index, int length); - /** - * Returns a slice of this buffer's readable bytes. Modifying the content - * of the returned buffer or this buffer affects each other's content - * while they maintain separate indexes and marks. - */ - Slice slice(); - /** * Returns a slice of this buffer's sub-region. Modifying the content of - * the returned buffer or this buffer affects each other's content while - * they maintain separate indexes and marks. + * the returned buffer or this buffer affects each other's content. */ Slice slice(int index, int length); @@ -291,13 +283,15 @@ int setBytes(int index, FileChannel in, int position, int length) /** * Converts this buffer's readable bytes into a NIO buffer. The returned - * buffer shares the content with this buffer. + * buffer shares the content with this buffer and is a slice (position + * is zero and limit is equal to capacity). */ ByteBuffer toByteBuffer(); /** * Converts this buffer's sub-region into a NIO buffer. The returned - * buffer shares the content with this buffer. + * buffer shares the content with this buffer and is a slice (position + * is zero and limit is equal to capacity). */ ByteBuffer toByteBuffer(int index, int length); diff --git a/src/main/java/com/facebook/presto/Slices.java b/src/main/java/com/facebook/presto/Slices.java index 2b22da0666b51..853d485c67836 100644 --- a/src/main/java/com/facebook/presto/Slices.java +++ b/src/main/java/com/facebook/presto/Slices.java @@ -18,7 +18,10 @@ package com.facebook.presto; import com.google.common.base.Preconditions; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; @@ -33,6 +36,12 @@ public final class Slices { + public static Slice mapFileReadOnly(File file) + throws IOException + { + return new ByteBufferSlice(Files.map(file)); + } + public static Slice readLengthPrefixedBytes(SliceInput sliceInput) { int length = VariableLengthQuantity.readVariableLengthInt(sliceInput); diff --git a/src/test/java/com/facebook/presto/TestSlice.java b/src/test/java/com/facebook/presto/TestSlice.java new file mode 100644 index 0000000000000..6cb74492e745c --- /dev/null +++ b/src/test/java/com/facebook/presto/TestSlice.java @@ -0,0 +1,49 @@ +package com.facebook.presto; + +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Longs; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Random; + +import static org.testng.Assert.assertEquals; + +public class TestSlice +{ + @Test + public void testMemoryMappedReads() + throws IOException + { + Path path = Files.createTempFile("longs", null); + ImmutableList values = createRandomLongs(20000); + + Slice output = Slices.allocate(values.size() * Longs.BYTES); + for (int i = 0; i < values.size(); i++) { + output.setLong(i * Longs.BYTES, values.get(i)); + } + + Files.write(path, output.getBytes()); + + Slice slice = Slices.mapFileReadOnly(path.toFile()); + for (int i = 0; i < values.size(); i++) { + long actual = slice.getLong(i * Longs.BYTES); + long expected = values.get(i); + assertEquals(actual, expected); + } + + assertEquals(slice.getBytes(), output.getBytes()); + } + + private static ImmutableList createRandomLongs(int count) + { + Random random = new Random(); + ImmutableList.Builder list = ImmutableList.builder(); + for (int i = 0; i < count; i++) { + list.add(random.nextLong()); + } + return list.build(); + } +}