blob: 041d46a135a5064ce99b99bfd7ad6bec8cb87e72 [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.ignite.internal.schema;
import static org.apache.ignite.internal.binarytuple.BinaryTupleCommon.PREFIX_FLAG;
import java.nio.ByteBuffer;
import org.apache.ignite.internal.binarytuple.BinaryTupleCommon;
import org.apache.ignite.internal.binarytuple.BinaryTuplePrefixBuilder;
import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
import org.apache.ignite.internal.lang.InternalTuple;
/**
* Class that represents a Binary Tuple Prefix.
*
* @see BinaryTuplePrefixBuilder BinaryTuplePrefixBuilder for information about the Binary Tuple Prefix format.
*/
public class BinaryTuplePrefix extends BinaryTupleReader implements InternalTuple {
/**
* Constructor.
*
* @param elementCount Number of tuple elements.
* @param bytes Serialized representation of a Binary Tuple Prefix.
*/
public BinaryTuplePrefix(int elementCount, byte[] bytes) {
super(elementCount, bytes);
}
/**
* Constructor.
*
* @param elementCount Number of tuple elements.
* @param buffer Serialized representation of a Binary Tuple Prefix.
*/
public BinaryTuplePrefix(int elementCount, ByteBuffer buffer) {
super(elementCount, buffer);
}
/**
* Creates a prefix from provided {@link BinaryTuple}. If given tuple has lesser or equal number of
* columns, then all elements will be used in resulting prefix. If given tuple has more columns, then
* excess columns will be truncated.
*
* @param numElements Number of elements in full schema of prefix.
* @param tuple Tuple to create a prefix from.
* @return Prefix, created from provided tuple with regards to desired number of elements.
*/
public static BinaryTuplePrefix fromBinaryTuple(int numElements, BinaryTuple tuple) {
if (numElements == tuple.elementCount()) {
return entireTuple(tuple);
} else if (numElements > tuple.elementCount()) {
return expandTuple(numElements, tuple);
} else {
return truncateTuple(numElements, tuple);
}
}
/**
* Creates a prefix that contains all columns from the provided {@link BinaryTuple}.
*
* @param tuple Tuple to create a prefix from.
* @return Prefix, equivalent to the tuple.
*/
public static BinaryTuplePrefix fromBinaryTuple(BinaryTuple tuple) {
return entireTuple(tuple);
}
private static BinaryTuplePrefix entireTuple(BinaryTuple tuple) {
ByteBuffer tupleBuffer = tuple.byteBuffer();
ByteBuffer prefixBuffer = ByteBuffer.allocate(tupleBuffer.remaining() + Integer.BYTES)
.order(ORDER)
.put(tupleBuffer)
.putInt(tuple.elementCount())
.flip();
byte flags = prefixBuffer.get(0);
prefixBuffer.put(0, (byte) (flags | PREFIX_FLAG));
return new BinaryTuplePrefix(tuple.elementCount(), prefixBuffer);
}
private static BinaryTuplePrefix expandTuple(int numElements, BinaryTuple tuple) {
assert numElements > tuple.elementCount();
if (tuple.elementCount() == 0) {
return new BinaryTuplePrefix(
numElements, new BinaryTuplePrefixBuilder(0, numElements, 0).build()
);
}
int[] dataBeginOffsetHolder = new int[1];
int[] dataEndOffsetHolder = new int[1];
tuple.fetch(0, (index, begin, end) -> dataBeginOffsetHolder[0] = begin);
tuple.fetch(tuple.elementCount() - 1, (index, begin, end) -> dataEndOffsetHolder[0] = end);
ByteBuffer tupleBuffer = tuple.byteBuffer();
byte flags = tupleBuffer.get(0);
int entrySize = BinaryTupleCommon.flagsToEntrySize(flags);
int newTupleSize = tupleBuffer.remaining() // size of original tuple
// additional space for the offset map to align it with desired number of columns
+ (entrySize * (numElements - tuple.elementCount()))
+ Integer.BYTES; // actual number of columns in prefix
ByteBuffer prefixBuffer = ByteBuffer.allocate(newTupleSize)
.order(ORDER)
.put(tupleBuffer.duplicate().limit(dataBeginOffsetHolder[0])); // header
int payloadEndPosition = dataEndOffsetHolder[0] - dataBeginOffsetHolder[0];
for (int idx = tuple.elementCount(); idx < numElements; idx++) {
switch (entrySize) {
case Byte.BYTES:
prefixBuffer.put((byte) payloadEndPosition);
break;
case Short.BYTES:
prefixBuffer.putShort((short) payloadEndPosition);
break;
case Integer.BYTES:
prefixBuffer.putInt(payloadEndPosition);
break;
default:
assert false;
}
}
prefixBuffer
.put(tupleBuffer.slice().position(dataBeginOffsetHolder[0]).limit(dataEndOffsetHolder[0])) // payload
.putInt(tuple.elementCount())
.flip();
prefixBuffer.put(0, (byte) (flags | PREFIX_FLAG));
return new BinaryTuplePrefix(numElements, prefixBuffer);
}
private static BinaryTuplePrefix truncateTuple(int numElements, BinaryTuple tuple) {
assert numElements < tuple.elementCount();
int[] dataBeginOffsetHolder = new int[1];
int[] dataEndOffsetHolder = new int[1];
tuple.fetch(0, (index, begin, end) -> dataBeginOffsetHolder[0] = begin);
tuple.fetch(numElements - 1, (index, begin, end) -> dataEndOffsetHolder[0] = end);
BinaryTuplePrefixBuilder builder = new BinaryTuplePrefixBuilder(
numElements, numElements, dataEndOffsetHolder[0] - dataBeginOffsetHolder[0]
);
for (int i = 0; i < numElements; i++) {
byte[] valueBytes = tuple.bytesValue(i);
builder.appendBytes(valueBytes);
}
return new BinaryTuplePrefix(numElements, builder.build());
}
@Override
public int elementCount() {
ByteBuffer buffer = byteBuffer();
return buffer.getInt(buffer.limit() - Integer.BYTES);
}
}