/* * Licensed 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 com.facebook.presto.spi.block; import io.airlift.slice.Slice; import java.util.ArrayList; import java.util.List; public abstract class AbstractInterleavedBlock implements Block { private final int columns; protected abstract Block getBlock(int blockIndex); protected abstract int toAbsolutePosition(int position); @Override public abstract InterleavedBlockEncoding getEncoding(); protected AbstractInterleavedBlock(int columns) { if (columns <= 0) { throw new IllegalArgumentException("Number of blocks in InterleavedBlock must be positive"); } this.columns = columns; } int getBlockCount() { return columns; } Block[] computeSerializableSubBlocks() { InterleavedBlock interleavedBlock = (InterleavedBlock) sliceRange(0, getPositionCount(), false); Block[] result = new Block[interleavedBlock.getBlockCount()]; for (int i = 0; i < result.length; i++) { result[i] = interleavedBlock.getBlock(i); } return result; } /** * Can only be called after the child class is initialized enough that getBlock will return the right value */ protected InterleavedBlockEncoding computeBlockEncoding() { BlockEncoding[] individualBlockEncodings = new BlockEncoding[columns]; for (int i = 0; i < columns; i++) { Block block = getBlock(i); individualBlockEncodings[i] = block.getEncoding(); } return new InterleavedBlockEncoding(individualBlockEncodings); } @Override public void writePositionTo(int position, BlockBuilder blockBuilder) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; getBlock(blockIndex).writePositionTo(positionInBlock, blockBuilder); } @Override public byte getByte(int position, int offset) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getByte(positionInBlock, offset); } @Override public short getShort(int position, int offset) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getShort(positionInBlock, offset); } @Override public int getInt(int position, int offset) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getInt(positionInBlock, offset); } @Override public long getLong(int position, int offset) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getLong(positionInBlock, offset); } @Override public Slice getSlice(int position, int offset, int length) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getSlice(positionInBlock, offset, length); } @Override public <T> T getObject(int position, Class<T> clazz) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getObject(positionInBlock, clazz); } @Override public int getSliceLength(int position) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).getSliceLength(positionInBlock); } @Override public boolean equals(int position, int offset, Block otherBlock, int otherPosition, int otherOffset, int length) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).equals(positionInBlock, offset, otherBlock, otherPosition, otherOffset, length); } @Override public boolean bytesEqual(int position, int offset, Slice otherSlice, int otherOffset, int length) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).bytesEqual(positionInBlock, offset, otherSlice, otherOffset, length); } @Override public long hash(int position, int offset, int length) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).hash(positionInBlock, offset, length); } @Override public int compareTo(int position, int offset, int length, Block otherBlock, int otherPosition, int otherOffset, int otherLength) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).compareTo(positionInBlock, offset, length, otherBlock, otherPosition, otherOffset, otherLength); } @Override public int bytesCompare(int position, int offset, int length, Slice otherSlice, int otherOffset, int otherLength) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).bytesCompare(positionInBlock, offset, length, otherSlice, otherOffset, otherLength); } @Override public void writeBytesTo(int position, int offset, int length, BlockBuilder blockBuilder) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; getBlock(blockIndex).writeBytesTo(positionInBlock, offset, length, blockBuilder); } @Override public Block getSingleValueBlock(int position) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; // return the underlying block directly, as it is unnecessary to wrap around it if there's only one block return getBlock(blockIndex).getSingleValueBlock(positionInBlock); } @Override public Block copyPositions(List<Integer> positions) { if (positions.size() % columns != 0) { throw new IllegalArgumentException("Positions.size (" + positions.size() + ") is not evenly dividable by columns (" + columns + ")"); } int positionsPerColumn = positions.size() / columns; List<List<Integer>> valuePositions = new ArrayList<>(columns); for (int i = 0; i < columns; i++) { valuePositions.add(new ArrayList<>(positionsPerColumn)); } int ordinal = 0; for (int position : positions) { position = toAbsolutePosition(position); if (ordinal % columns != position % columns) { throw new IllegalArgumentException("Position (" + position + ") is not congruent to ordinal (" + ordinal + ") modulo columns (" + columns + ")"); } valuePositions.get(position % columns).add(position / columns); ordinal++; } Block[] blocks = new Block[columns]; for (int i = 0; i < columns; i++) { blocks[i] = getBlock(i).copyPositions(valuePositions.get(i)); } return new InterleavedBlock(blocks); } @Override public Block copyRegion(int position, int length) { validateRange(position, length); return sliceRange(position, length, true); } @Override public int getRegionSizeInBytes(int position, int length) { if (position == 0 && length == getPositionCount()) { // Calculation of getRegionSizeInBytes is expensive in this class. // On the other hand, getSizeInBytes result is cached or pre-computed. return getSizeInBytes(); } validateRange(position, length); int result = 0; for (int blockIndex = 0; blockIndex < getBlockCount(); blockIndex++) { result += getBlock(blockIndex).getRegionSizeInBytes(position / columns, length / columns); } return result; } protected void validateRange(int position, int length) { int positionCount = getPositionCount(); if (position < 0 || length < 0 || position + length > positionCount || position % columns != 0 || length % columns != 0) { throw new IndexOutOfBoundsException("Invalid position (" + position + "), length (" + length + ") in InterleavedBlock with " + positionCount + " positions and " + columns + " columns"); } } protected Block sliceRange(int position, int length, boolean compact) { position = toAbsolutePosition(position); Block[] resultBlocks = new Block[columns]; int positionInBlock = position / columns; int subBlockLength = length / columns; for (int blockIndex = 0; blockIndex < columns; blockIndex++) { if (compact) { resultBlocks[blockIndex] = getBlock((blockIndex + position) % columns).copyRegion(positionInBlock, subBlockLength); } else { resultBlocks[blockIndex] = getBlock((blockIndex + position) % columns).getRegion(positionInBlock, subBlockLength); } } return new InterleavedBlock(resultBlocks); } @Override public boolean isNull(int position) { position = toAbsolutePosition(position); int blockIndex = position % columns; int positionInBlock = position / columns; return getBlock(blockIndex).isNull(positionInBlock); } }