io.deephaven.engine.table.impl.sources.regioned.ColumnRegionObject Maven / Gradle / Ivy
Show all versions of deephaven-engine-table Show documentation
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.engine.table.impl.sources.regioned;
import io.deephaven.chunk.attributes.Any;
import io.deephaven.engine.page.PagingContextHolder;
import io.deephaven.engine.table.impl.chunkattributes.DictionaryKeys;
import io.deephaven.chunk.ChunkType;
import io.deephaven.chunk.WritableChunk;
import io.deephaven.chunk.WritableLongChunk;
import io.deephaven.engine.page.Page;
import io.deephaven.engine.rowset.RowSequence;
import io.deephaven.engine.rowset.RowSet;
import io.deephaven.engine.rowset.RowSetBuilderSequential;
import io.deephaven.util.annotations.FinalDefault;
import org.jetbrains.annotations.NotNull;
import java.util.stream.IntStream;
import static io.deephaven.util.QueryConstants.NULL_LONG;
/**
* Column region interface for regions that support fetching objects.
*/
public interface ColumnRegionObject extends ColumnRegion {
/**
* Get a single object from this region.
*
* @param elementIndex Element row key in the table's address space
* @return The object value at the specified element row key
*/
DATA_TYPE getObject(long elementIndex);
/**
* Get a single object from this region.
*
* @param context A {@link PagingContextHolder} to enable resource caching where suitable, with current
* region index pointing to this region
* @param elementIndex Element row key in the table's address space
* @return The object value at the specified element row key
*/
default DATA_TYPE getObject(@NotNull final FillContext context, final long elementIndex) {
return getObject(elementIndex);
}
/**
* Check if this region can expose an alternate form as paired regions of {@code long} keys and {@code DATA_TYPE}
* values covering all of its row keys in {@code keysToVisit}.
*
* Both alternate regions must use the same or smaller row key space as this one. Indices fetched from the
* keys region must represent valid element indices in the values region. Values regions must support
* {@link #gatherDictionaryValuesRowSet(RowSet.SearchIterator, RowSequence.Iterator, RowSetBuilderSequential)}.
*
*
Use {@link #getDictionaryKeysRegion()} to access the region of keys and {@link #getDictionaryValuesRegion()}
* to access the region of values.
*
* @param keysToVisit Iterator positioned at the first relevant row key belonging to this region.
* Will be advanced to after this region if {@code true} is returned.
* No guarantee is made if {@code false} is returned.
* @return A {@link RegionVisitResult} specifying {@code FAILED} if this region cannot supply a dictionary,
* {@code CONTINUE} if it can and {@code keysToVisit} is not exhausted, and {@code COMPLETE} if it can and
* {@code keysToVisit} is exhausted
*/
default RegionVisitResult supportsDictionaryFormat(@NotNull final RowSet.SearchIterator keysToVisit) {
return RegionVisitResult.FAILED;
}
/**
* Optional method that should only be used on regions returned by {@link #getDictionaryValuesRegion()}.
* Gathers row keys representing the dictionary values for this region, excluding those already known to the caller.
* This is used to support {@link SymbolTableSource symbol table} access.
*
* @param keysToVisit A search iterator over the enclosing table address space (which must have the same
* regions at the same masks), positioned at a row key in this region. Used to
* identify regions to visit. Should be advanced to after this region as a side-effect.
* @param knownKeys An iterator over the previously-known row keys, positioned at the first known key in
* this region, or after the region's maximum key if no keys are known. Should be advanced
* to after this region as a side effect.
* @param sequentialBuilder Output builder; implementations should append ranges for row keys not found in
* {@code knownKeys}
* @throws UnsupportedOperationException If this region is incapable of gathering its dictionary values RowSet
* @return Whether {@code keysToVisit} has been exhausted
*/
default boolean gatherDictionaryValuesRowSet(@NotNull final RowSet.SearchIterator keysToVisit,
@NotNull final RowSequence.Iterator knownKeys,
@NotNull final RowSetBuilderSequential sequentialBuilder) {
throw new UnsupportedOperationException();
}
/**
* @return A dictionary keys region as specified by {@link #supportsDictionaryFormat(RowSet.SearchIterator)}
* @throws UnsupportedOperationException If this region does not support dictionary format
* @implNote Implementations should cache the result
*/
default ColumnRegionLong getDictionaryKeysRegion() {
throw new UnsupportedOperationException();
}
/**
* @return A dictionary values region as specified by {@link #supportsDictionaryFormat(RowSet.SearchIterator)}
* @throws UnsupportedOperationException If this region does not support dictionary format
* @implNote Implementations should cache the result
*/
default ColumnRegionObject getDictionaryValuesRegion() {
throw new UnsupportedOperationException();
}
@Override
@FinalDefault
default ChunkType getChunkType() {
return ChunkType.Object;
}
interface SelfDictionaryRegion extends ColumnRegionObject {
@Override
@FinalDefault
default RegionVisitResult supportsDictionaryFormat(@NotNull final RowSet.SearchIterator keysToVisit) {
return advanceToNextPage(keysToVisit) ? RegionVisitResult.CONTINUE : RegionVisitResult.COMPLETE;
}
@Override
@FinalDefault
default ColumnRegionObject getDictionaryValuesRegion() {
return this;
}
}
static ColumnRegionObject createNull(final long pageMask) {
//noinspection unchecked
return pageMask == Null.DEFAULT_INSTANCE.mask() ? Null.DEFAULT_INSTANCE : new Null(pageMask);
}
final class Null extends ColumnRegion.Null implements SelfDictionaryRegion {
@SuppressWarnings("rawtypes")
private static final ColumnRegionObject DEFAULT_INSTANCE = new ColumnRegionObject.Null(RegionedColumnSourceBase.PARAMETERS.regionMask);
private ColumnRegionLong dictionaryKeysRegion;
private Null(final long pageMask) {
super(pageMask);
}
@Override
public DATA_TYPE getObject(final long elementIndex) {
return null;
}
@Override
public boolean gatherDictionaryValuesRowSet(@NotNull final RowSet.SearchIterator keysToVisit,
@NotNull final RowSequence.Iterator knownKeys,
@NotNull final RowSetBuilderSequential sequentialBuilder) {
// Nothing to be gathered, we don't include null regions in dictionary values.
advanceToNextPage(knownKeys);
return advanceToNextPage(keysToVisit);
}
@Override
public ColumnRegionLong getDictionaryKeysRegion() {
return dictionaryKeysRegion == null ? dictionaryKeysRegion = ColumnRegionLong.createNull(mask()) : dictionaryKeysRegion;
}
}
static ColumnRegionLong createConstantDictionaryKeysRegion(final long pageMask) {
return pageMask == Constant.DEFAULT_SINGLETON_DICTIONARY_KEYS_REGION.mask()
? Constant.DEFAULT_SINGLETON_DICTIONARY_KEYS_REGION
: new ColumnRegionLong.Constant<>(pageMask, 0L);
}
final class Constant
extends GenericColumnRegionBase
implements SelfDictionaryRegion, WithDefaultsForRepeatingValues {
private static final ColumnRegionLong DEFAULT_SINGLETON_DICTIONARY_KEYS_REGION = new ColumnRegionLong.Constant<>(RegionedColumnSourceBase.PARAMETERS.regionMask, 0L);
private final DATA_TYPE value;
private ColumnRegionLong dictionaryKeysRegion;
public Constant(final long pageMask, final DATA_TYPE value) {
super(pageMask);
this.value = value;
}
@Override
public DATA_TYPE getObject(final long elementIndex) {
return value;
}
@Override
public void fillChunkAppend(@NotNull final FillContext context, @NotNull final WritableChunk super ATTR> destination, final int length) {
final int offset = destination.size();
destination.asWritableObjectChunk().fillWithValue(offset, length, value);
destination.setSize(offset + length);
}
@Override
public boolean gatherDictionaryValuesRowSet(@NotNull final RowSet.SearchIterator keysToVisit,
@NotNull final RowSequence.Iterator knownKeys,
@NotNull final RowSetBuilderSequential sequentialBuilder) {
final long pageOnlyKey = firstRow(keysToVisit.currentValue());
if (knownKeys.peekNextKey() != pageOnlyKey) {
sequentialBuilder.appendKey(pageOnlyKey);
}
advanceToNextPage(knownKeys);
return advanceToNextPage(keysToVisit);
}
@Override
public ColumnRegionLong getDictionaryKeysRegion() {
return dictionaryKeysRegion == null ? dictionaryKeysRegion = createConstantDictionaryKeysRegion(mask()) : dictionaryKeysRegion;
}
}
final class StaticPageStore
extends RegionedPageStore.Static>
implements ColumnRegionObject {
private ColumnRegionLong dictionaryKeysRegion;
private ColumnRegionObject dictionaryValuesRegion;
public StaticPageStore(@NotNull final Parameters parameters, @NotNull final ColumnRegionObject[] regions) {
super(parameters, regions);
}
@Override
public void invalidate() {
for(int ii = 0; ii < getRegionCount(); ii++) {
getRegion(ii).invalidate();
}
}
@Override
public DATA_TYPE getObject(final long elementIndex) {
return lookupRegion(elementIndex).getObject(elementIndex);
}
@Override
public DATA_TYPE getObject(@NotNull final FillContext context, final long elementIndex) {
return lookupRegion(elementIndex).getObject(context, elementIndex);
}
@Override
public RegionVisitResult supportsDictionaryFormat(@NotNull final RowSet.SearchIterator keysToVisit) {
final long pageMaxKey = maxRow(keysToVisit.currentValue());
RegionVisitResult result;
do {
result = lookupRegion(keysToVisit.currentValue()).supportsDictionaryFormat(keysToVisit);
} while (result == RegionVisitResult.CONTINUE && keysToVisit.currentValue() <= pageMaxKey);
return result;
}
@Override
public boolean gatherDictionaryValuesRowSet(@NotNull final RowSet.SearchIterator keysToVisit,
@NotNull final RowSequence.Iterator knownKeys,
@NotNull final RowSetBuilderSequential sequentialBuilder) {
final long pageMaxKey = maxRow(keysToVisit.currentValue());
boolean moreKeysToVisit;
do {
moreKeysToVisit = lookupRegion(keysToVisit.currentValue()).gatherDictionaryValuesRowSet(keysToVisit, knownKeys, sequentialBuilder);
} while (moreKeysToVisit && keysToVisit.currentValue() <= pageMaxKey);
return moreKeysToVisit;
}
@Override
public ColumnRegionLong getDictionaryKeysRegion() {
return dictionaryKeysRegion == null
? dictionaryKeysRegion = new ColumnRegionLong.StaticPageStore<>(parameters(), mapRegionsToDictionaryKeys())
: dictionaryKeysRegion;
}
@Override
public ColumnRegionObject getDictionaryValuesRegion() {
return dictionaryValuesRegion == null
? dictionaryValuesRegion = new ColumnRegionObject.StaticPageStore<>(parameters(), mapRegionsToDictionaryValues())
: dictionaryValuesRegion;
}
private ColumnRegionLong[] mapRegionsToDictionaryKeys() {
//noinspection unchecked
return IntStream.range(0, getRegionCount())
.mapToObj(ri -> DictionaryKeysWrapper.create(parameters(), ri, getRegion(ri)))
.toArray(ColumnRegionLong[]::new);
}
private ColumnRegionObject[] mapRegionsToDictionaryValues() {
//noinspection unchecked
return IntStream.range(0, getRegionCount())
.mapToObj(ri -> getRegion(ri).getDictionaryValuesRegion())
.toArray(ColumnRegionObject[]::new);
}
}
final class DictionaryKeysWrapper implements ColumnRegionLong, Page.WithDefaults {
public static ColumnRegionLong create(@NotNull final RegionedPageStore.Parameters parameters,
final int regionIndex,
@NotNull final ColumnRegionObject, ?> sourceRegion) {
final ColumnRegionLong sourceDictKeys = sourceRegion.getDictionaryKeysRegion();
if (sourceDictKeys instanceof ColumnRegionLong.Null) {
return sourceDictKeys;
}
return new DictionaryKeysWrapper((long) regionIndex << parameters.regionMaskNumBits, sourceDictKeys);
}
private final long prefixBits;
private final ColumnRegionLong wrapped;
private DictionaryKeysWrapper(final long prefixBits, @NotNull final ColumnRegionLong wrapped) {
this.prefixBits = prefixBits;
this.wrapped = wrapped;
}
@Override
public void invalidate() {
wrapped.invalidate();
}
@Override
public long mask() {
return wrapped.mask();
}
@Override
public long getLong(final long elementIndex) {
final long dictionaryKey = wrapped.getLong(elementIndex);
return dictionaryKey == NULL_LONG ? NULL_LONG : prefixBits | dictionaryKey;
}
@Override
public long getLong(@NotNull final FillContext context, final long elementIndex) {
final long dictionaryKey = wrapped.getLong(context, elementIndex);
return dictionaryKey == NULL_LONG ? NULL_LONG : prefixBits | dictionaryKey;
}
@Override
public void fillChunkAppend(@NotNull final FillContext context, @NotNull final WritableChunk super DictionaryKeys> destination, @NotNull final RowSequence rowSequence) {
final WritableLongChunk super DictionaryKeys> typed = destination.asWritableLongChunk();
final int firstOffsetInclusive = destination.size();
try (final RowSequence.Iterator rsi = rowSequence.getRowSequenceIterator()) {
wrapped.fillChunkAppend(context, destination, rsi);
}
final int lastOffsetExclusive = destination.size();
for (int dki = firstOffsetInclusive; dki < lastOffsetExclusive; ++dki) {
final long dictionaryKey = typed.get(dki);
if (dictionaryKey != NULL_LONG) {
typed.set(dki, prefixBits | dictionaryKey);
}
}
}
}
}