public ByteBuffersDataInput slice(long offset, long length) { if (offset < 0 || length < 0 || offset + length > this.size) { throw new IllegalArgumentException(String.format(Locale.ROOT, "slice(offset=%s, length=%s) is out of bounds: %s", offset, length, this)); } return new ByteBuffersDataInput(sliceBufferList(Arrays.asList(this.blocks), offset, length)); }
@Override public byte readByte() throws EOFException { try { ByteBuffer block = blocks[blockIndex(pos)]; byte v = block.get(blockOffset(pos)); pos++; return v; } catch (IndexOutOfBoundsException e) { if (pos >= size()) { throw new EOFException(); } else { throw e; // Something is wrong. } } }
@Override public short readShort(long pos) { long absPos = offset + pos; int blockOffset = blockOffset(absPos); if (blockOffset + Short.BYTES <= blockMask) { return blocks[blockIndex(absPos)].getShort(blockOffset); } else { return (short) ((readByte(pos ) & 0xFF) << 8 | (readByte(pos + 1) & 0xFF)); } }
@Override public long readLong(long pos) { long absPos = offset + pos; int blockOffset = blockOffset(absPos); if (blockOffset + Long.BYTES <= blockMask) { return blocks[blockIndex(absPos)].getLong(blockOffset); } else { return (((long) readInt(pos)) << 32) | (readInt(pos + 4) & 0xFFFFFFFFL); } }
@Override public byte readByte(long pos) { pos += offset; return blocks[blockIndex(pos)].get(blockOffset(pos)); }
/** * Read data from a set of contiguous buffers. All data buffers except for the last one * must have an identical remaining number of bytes in the buffer (that is a power of two). The last * buffer can be of an arbitrary remaining length. */ public ByteBuffersDataInput(List<ByteBuffer> buffers) { ensureAssumptions(buffers); this.blocks = buffers.stream().map(buf -> buf.asReadOnlyBuffer()).toArray(ByteBuffer[]::new); if (blocks.length == 1) { this.blockBits = 32; this.blockMask = ~0; } else { final int blockBytes = determineBlockPage(buffers); this.blockBits = Integer.numberOfTrailingZeros(blockBytes); this.blockMask = (1 << blockBits) - 1; } this.size = Arrays.stream(blocks).mapToLong(block -> block.remaining()).sum(); // The initial "position" of this stream is shifted by the position of the first block. this.offset = blocks[0].position(); this.pos = offset; }
private static void ensureAssumptions(List<ByteBuffer> buffers) { if (buffers.isEmpty()) { throw new IllegalArgumentException("Buffer list must not be empty."); } if (buffers.size() == 1) { // Special case of just a single buffer, conditions don't apply. } else { final int blockPage = determineBlockPage(buffers); // First buffer decides on block page length. if (!isPowerOfTwo(blockPage)) { throw new IllegalArgumentException("The first buffer must have power-of-two position() + remaining(): 0x" + Integer.toHexString(blockPage)); } // Any block from 2..last-1 should have the same page size. for (int i = 1, last = buffers.size() - 1; i < last; i++) { ByteBuffer buffer = buffers.get(i); if (buffer.position() != 0) { throw new IllegalArgumentException("All buffers except for the first one must have position() == 0: " + buffer); } if (i != last && buffer.remaining() != blockPage) { throw new IllegalArgumentException("Intermediate buffers must share an identical remaining() power-of-two block size: 0x" + Integer.toHexString(blockPage)); } } } }
@Override public void readBytes(byte[] b, int offset, int len) throws IOException { ensureOpen(); in.readBytes(b, offset, len); }
@Override public byte readByte(long pos) throws IOException { ensureOpen(); return in.readByte(pos); }
/** * Return a {@link ByteBuffersDataInput} for the set of current buffers ({@link #toBufferList()}). */ public ByteBuffersDataInput toDataInput() { return new ByteBuffersDataInput(toBufferList()); }
@Override public long getFilePointer() { ensureOpen(); return in.position(); }
@Override public int readInt(long pos) { long absPos = offset + pos; int blockOffset = blockOffset(absPos); if (blockOffset + Integer.BYTES <= blockMask) { return blocks[blockIndex(absPos)].getInt(blockOffset); } else { return ((readByte(pos ) ) << 24 | (readByte(pos + 1) & 0xFF) << 16 | (readByte(pos + 2) & 0xFF) << 8 | (readByte(pos + 3) & 0xFF)); } }
@Override public long readLong(long pos) { long absPos = offset + pos; int blockOffset = blockOffset(absPos); if (blockOffset + Long.BYTES <= blockMask) { return blocks[blockIndex(absPos)].getLong(blockOffset); } else { return (((long) readInt(pos)) << 32) | (readInt(pos + 4) & 0xFFFFFFFFL); } }
private static List<ByteBuffer> sliceBufferList(List<ByteBuffer> buffers, long offset, long length) { ensureAssumptions(buffers); long absEnd = Math.toIntExact(absStart + length); int blockBytes = ByteBuffersDataInput.determineBlockPage(buffers); int blockBits = Integer.numberOfTrailingZeros(blockBytes); int blockMask = (1 << blockBits) - 1;
@Override public byte readByte(long pos) { pos += offset; return blocks[blockIndex(pos)].get(blockOffset(pos)); }
private static void ensureAssumptions(List<ByteBuffer> buffers) { if (buffers.isEmpty()) { throw new IllegalArgumentException("Buffer list must not be empty."); } if (buffers.size() == 1) { // Special case of just a single buffer, conditions don't apply. } else { final int blockPage = determineBlockPage(buffers); // First buffer decides on block page length. if (!isPowerOfTwo(blockPage)) { throw new IllegalArgumentException("The first buffer must have power-of-two position() + remaining(): 0x" + Integer.toHexString(blockPage)); } // Any block from 2..last-1 should have the same page size. for (int i = 1, last = buffers.size() - 1; i < last; i++) { ByteBuffer buffer = buffers.get(i); if (buffer.position() != 0) { throw new IllegalArgumentException("All buffers except for the first one must have position() == 0: " + buffer); } if (i != last && buffer.remaining() != blockPage) { throw new IllegalArgumentException("Intermediate buffers must share an identical remaining() power-of-two block size: 0x" + Integer.toHexString(blockPage)); } } } }
@Override public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException { ensureOpen(); in.readBytes(b, offset, len, useBuffer); }
@Override public byte readByte() throws IOException { ensureOpen(); return in.readByte(); }
/** * Copy the current content of this object into another {@link DataOutput}. */ public void copyTo(DataOutput output) throws IOException { for (ByteBuffer bb : toBufferList()) { if (bb.hasArray()) { output.writeBytes(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining()); } else { output.copyBytes(new ByteBuffersDataInput(Arrays.asList(bb)), bb.remaining()); } } }