/*
* 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.cassandra.db.composites;
import java.io.DataInput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Comparator;
import org.apache.cassandra.db.Cell;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.CompositeType;
public abstract class AbstractCompoundCellNameType extends AbstractCellNameType
{
protected final CompoundCType clusteringType;
protected final CompoundCType fullType;
protected final int clusteringSize;
protected final int fullSize;
protected AbstractCompoundCellNameType(CompoundCType clusteringType, CompoundCType fullType)
{
super(isByteOrderComparable(fullType.types));
this.clusteringType = clusteringType;
this.fullType = fullType;
this.clusteringSize = clusteringType.size();
this.fullSize = fullType.size();
}
public int clusteringPrefixSize()
{
return clusteringSize;
}
public boolean isCompound()
{
return true;
}
public int size()
{
return fullSize;
}
public AbstractType<?> subtype(int i)
{
return fullType.subtype(i);
}
public CBuilder prefixBuilder()
{
return clusteringType.builder();
}
public CBuilder builder()
{
return new CompoundCType.CompoundCBuilder(this);
}
@Override
public Composite fromByteBuffer(ByteBuffer bytes)
{
if (!bytes.hasRemaining())
return Composites.EMPTY;
ByteBuffer[] elements = new ByteBuffer[fullSize];
int idx = bytes.position(), i = 0;
byte eoc = 0;
boolean isStatic = false;
if (CompositeType.isStaticName(bytes))
{
isStatic = true;
idx += 2;
}
while (idx < bytes.limit())
{
checkRemaining(bytes, idx, 2);
int length = bytes.getShort(idx) & 0xFFFF;
idx += 2;
checkRemaining(bytes, idx, length + 1);
elements[i++] = sliceBytes(bytes, idx, length);
idx += length;
eoc = bytes.get(idx++);
}
return makeWith(elements, i, Composite.EOC.from(eoc), isStatic);
}
public AbstractType<?> asAbstractType()
{
return CompositeType.getInstance(fullType.types);
}
public Deserializer newDeserializer(DataInput in)
{
return new CompositeDeserializer(this, in);
}
protected CellName makeCellName(ByteBuffer[] components)
{
return (CellName)makeWith(components, components.length, Composite.EOC.NONE, false);
}
protected abstract Composite makeWith(ByteBuffer[] components, int size, Composite.EOC eoc, boolean isStatic);
protected abstract Composite copyAndMakeWith(ByteBuffer[] components, int size, Composite.EOC eoc, boolean isStatic);
private static class CompositeDeserializer implements CellNameType.Deserializer
{
private static byte[] EMPTY = new byte[0];
private final AbstractCompoundCellNameType type;
private final DataInput in;
private byte[] nextFull;
private int nextIdx;
private final ByteBuffer[] nextComponents;
private int nextSize;
private Composite.EOC nextEOC;
private boolean nextIsStatic;
public CompositeDeserializer(AbstractCompoundCellNameType type, DataInput in)
{
this.type = type;
this.in = in;
this.nextComponents = new ByteBuffer[type.size()];
}
public boolean hasNext() throws IOException
{
if (nextFull == null)
maybeReadNext();
return nextFull != EMPTY;
}
public boolean hasUnprocessed() throws IOException
{
return nextFull != null;
}
public int compareNextTo(Composite composite) throws IOException
{
maybeReadNext();
if (composite.isEmpty())
return nextFull == EMPTY ? 0 : 1;
if (nextFull == EMPTY)
return -1;
if (nextIsStatic != composite.isStatic())
return nextIsStatic ? -1 : 1;
ByteBuffer previous = null;
for (int i = 0; i < composite.size(); i++)
{
if (!hasComponent(i))
return nextEOC == Composite.EOC.END ? 1 : -1;
AbstractType<?> comparator = type.subtype(i);
ByteBuffer value1 = nextComponents[i];
ByteBuffer value2 = composite.get(i);
int cmp = comparator.compareCollectionMembers(value1, value2, previous);
if (cmp != 0)
return cmp;
previous = value1;
}
// If we have more component than composite
if (!allComponentsDeserialized() || composite.size() < nextSize)
return composite.eoc() == Composite.EOC.END ? -1 : 1;
// same size, check eoc
if (nextEOC != composite.eoc())
{
switch (nextEOC)
{
case START: return -1;
case END: return 1;
case NONE: return composite.eoc() == Composite.EOC.START ? 1 : -1;
}
}
return 0;
}
private boolean hasComponent(int i)
{
while (i >= nextSize && deserializeOne())
continue;
return i < nextSize;
}
private int readShort()
{
return ((nextFull[nextIdx++] & 0xFF) << 8) | (nextFull[nextIdx++] & 0xFF);
}
private int peekShort()
{
return ((nextFull[nextIdx] & 0xFF) << 8) | (nextFull[nextIdx+1] & 0xFF);
}
private boolean deserializeOne()
{
if (allComponentsDeserialized())
return false;
int length = readShort();
ByteBuffer component = ByteBuffer.wrap(nextFull, nextIdx, length);
nextIdx += length;
nextComponents[nextSize++] = component;
nextEOC = Composite.EOC.from(nextFull[nextIdx++]);
return true;
}
private void deserializeAll()
{
while (deserializeOne())
continue;
}
private boolean allComponentsDeserialized()
{
return nextIdx >= nextFull.length;
}
private void maybeReadNext() throws IOException
{
if (nextFull != null)
return;
nextIdx = 0;
nextSize = 0;
int length = in.readShort() & 0xFFFF;
// Note that empty is ok because it marks the end of row
if (length == 0)
{
nextFull = EMPTY;
return;
}
nextFull = new byte[length];
in.readFully(nextFull);
// Is is a static?
nextIsStatic = false;
if (peekShort() == CompositeType.STATIC_MARKER)
{
nextIsStatic = true;
readShort(); // Skip the static marker
}
}
public Composite readNext() throws IOException
{
maybeReadNext();
if (nextFull == EMPTY)
return Composites.EMPTY;
deserializeAll();
Composite c = type.copyAndMakeWith(nextComponents, nextSize, nextEOC, nextIsStatic);
nextFull = null;
return c;
}
public void skipNext() throws IOException
{
maybeReadNext();
nextFull = null;
}
}
}