From e1ef9ad9a953f26cb424f65a1460e9b0f611670c Mon Sep 17 00:00:00 2001 From: Ryan Caudy Date: Tue, 5 Dec 2023 08:41:52 -0800 Subject: [PATCH] RWC Changes --- .../CompoundDataIndexBoxedKeySource.java | 120 ++++++++++++++++++ .../DataIndexBoxedKeyHashAdapter.java | 40 ++++++ .../table/impl/dataindex/DataIndexUtils.java | 29 +++++ .../SingleDataIndexBoxedKeySource.java | 98 ++++++++++++++ .../hierarchical/RollupNodeKeySource.java | 4 +- 5 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/CompoundDataIndexBoxedKeySource.java create mode 100644 engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexBoxedKeyHashAdapter.java create mode 100644 engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexUtils.java create mode 100644 engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/SingleDataIndexBoxedKeySource.java diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/CompoundDataIndexBoxedKeySource.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/CompoundDataIndexBoxedKeySource.java new file mode 100644 index 00000000000..bbf7fdaee09 --- /dev/null +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/CompoundDataIndexBoxedKeySource.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending + */ +package io.deephaven.engine.table.impl.dataindex; + +import io.deephaven.chunk.*; +import io.deephaven.chunk.attributes.Values; +import io.deephaven.engine.rowset.RowSequence; +import io.deephaven.engine.table.ChunkSource; +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.SharedContext; +import io.deephaven.engine.table.impl.DefaultChunkSource; +import io.deephaven.engine.table.impl.chunkboxer.ChunkBoxer; +import io.deephaven.engine.table.impl.sources.ReinterpretUtils; +import io.deephaven.util.SafeCloseable; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * {@link ChunkSource} that produces data index {@link io.deephaven.engine.table.DataIndex.RowSetLookup row set lookup} + * keys from multiple {@link ColumnSource sources}. This can be used to extract keys from a data index table, or from a + * table of probe values. + */ +final class CompoundDataIndexBoxedKeySource implements DefaultChunkSource.WithPrev { + + private final ColumnSource[] keySources; + private final int keyWidth; + + /** + * Construct a new CompoundDataIndexBoxedKeySource backed by the supplied {@link ColumnSource column sources}. + * + * @param keySources Sources corresponding to the key columns + */ + CompoundDataIndexBoxedKeySource(@NotNull final ColumnSource... keySources) { + this.keySources = Arrays.stream(keySources) + .map(ReinterpretUtils::maybeConvertToPrimitive).toArray(ColumnSource[]::new); + keyWidth = keySources.length; + } + + @Override + public ChunkType getChunkType() { + return ChunkType.Object; + } + + @Override + public void fillChunk( + @NotNull final ChunkSource.FillContext context, + @NotNull final WritableChunk destination, + @NotNull final RowSequence rowSequence) { + fillChunkInternal(context, destination, rowSequence, false); + } + + public void fillPrevChunk( + @NotNull final ChunkSource.FillContext context, + @NotNull final WritableChunk destination, + @NotNull final RowSequence rowSequence) { + fillChunkInternal(context, destination, rowSequence, true); + } + + private void fillChunkInternal( + @NotNull final ChunkSource.FillContext context, + @NotNull final WritableChunk destination, + @NotNull final RowSequence rowSequence, + final boolean usePrev) { + if (rowSequence.isEmpty()) { + destination.setSize(0); + return; + } + final FillContext fc = (FillContext) context; + for (int ci = 0; ci < keyWidth; ++ci) { + fc.boxedKeys[ci] = fc.keyBoxers[ci].box(usePrev + ? keySources[ci].getPrevChunk(fc.keyContexts[ci], rowSequence) + : keySources[ci].getChunk(fc.keyContexts[ci], rowSequence)); + } + final int size = rowSequence.intSize(); + destination.setSize(size); + final WritableObjectChunk typedDestination = destination.asWritableObjectChunk(); + for (int ri = 0; ri < size; ++ri) { + final Object[] columnValues = new Object[keyWidth]; + for (int ci = 0; ci < keyWidth; ++ci) { + columnValues[ci] = fc.boxedKeys[ci].get(ri); + } + typedDestination.set(ri, columnValues); + } + } + + private static class FillContext implements ChunkSource.FillContext { + + private final GetContext[] keyContexts; + private final ChunkBoxer.BoxerKernel[] keyBoxers; + private final ObjectChunk[] boxedKeys; + + private FillContext( + final int chunkCapacity, + @NotNull final ColumnSource[] keySources, + final SharedContext sharedContext) { + keyContexts = Stream.of(keySources) + .map(cs -> cs.makeGetContext(chunkCapacity, sharedContext)) + .toArray(GetContext[]::new); + keyBoxers = Stream.of(keySources) + .map(cs -> ChunkBoxer.getBoxer(cs.getChunkType(), chunkCapacity)) + .toArray(ChunkBoxer.BoxerKernel[]::new); + // noinspection unchecked + boxedKeys = new ObjectChunk[keySources.length]; + } + + @Override + public void close() { + SafeCloseable.closeAll(keyContexts); + SafeCloseable.closeAll(keyBoxers); + } + } + + @Override + public ChunkSource.FillContext makeFillContext(final int chunkCapacity, final SharedContext sharedContext) { + return new FillContext(chunkCapacity, keySources, sharedContext); + } +} diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexBoxedKeyHashAdapter.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexBoxedKeyHashAdapter.java new file mode 100644 index 00000000000..dfc3df1af0c --- /dev/null +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexBoxedKeyHashAdapter.java @@ -0,0 +1,40 @@ +package io.deephaven.engine.table.impl.dataindex; + +import io.deephaven.hash.KeyedObjectKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * {@link KeyedObjectKey} implementation for {@link io.deephaven.engine.table.DataIndex} keys when boxed for hash-based + * lookup. + */ +public class DataIndexBoxedKeyHashAdapter implements KeyedObjectKey { + + public static final KeyedObjectKey INSTANCE = new DataIndexBoxedKeyHashAdapter(); + + private DataIndexBoxedKeyHashAdapter() {} + + @Override + public Object getKey(@NotNull final Object value) { + return value; + } + + @Override + public int hashKey(@Nullable final Object key) { + if (key instanceof Object[]) { + return Arrays.hashCode((Object[]) key); + } + return Objects.hashCode(key); + } + + @Override + public boolean equalKey(@Nullable final Object key, @NotNull final Object value) { + if (key instanceof Object[] && value instanceof Object[]) { + return Arrays.equals((Object[]) key, (Object[]) value); + } + return Objects.equals(key, value); + } +} diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexUtils.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexUtils.java new file mode 100644 index 00000000000..8c88a8b1eaf --- /dev/null +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/DataIndexUtils.java @@ -0,0 +1,29 @@ +package io.deephaven.engine.table.impl.dataindex; + +import io.deephaven.chunk.attributes.Values; +import io.deephaven.engine.table.ChunkSource; +import io.deephaven.engine.table.ColumnSource; + +/** + * Tools for working with {@link io.deephaven.engine.table.DataIndex data indices}. + */ +public class DataIndexUtils { + + /** + * Make a {@link ChunkSource} that produces data index {@link io.deephaven.engine.table.DataIndex.RowSetLookup row + * set lookup} keys from {@code keySources}. + * + * @param keySources The individual key sources + * @return The boxed key source + */ + public static ChunkSource.WithPrev makeBoxedKeySource(final ColumnSource... keySources) { + switch (keySources.length) { + case 0: + throw new IllegalArgumentException("Data index must have at least one key column"); + case 1: + return new SingleDataIndexBoxedKeySource(keySources[0]); + default: + return new CompoundDataIndexBoxedKeySource(keySources); + } + } +} diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/SingleDataIndexBoxedKeySource.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/SingleDataIndexBoxedKeySource.java new file mode 100644 index 00000000000..295fa679c21 --- /dev/null +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/dataindex/SingleDataIndexBoxedKeySource.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending + */ +package io.deephaven.engine.table.impl.dataindex; + +import io.deephaven.chunk.ChunkType; +import io.deephaven.chunk.ObjectChunk; +import io.deephaven.chunk.WritableChunk; +import io.deephaven.chunk.attributes.Values; +import io.deephaven.engine.rowset.RowSequence; +import io.deephaven.engine.table.ChunkSource; +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.SharedContext; +import io.deephaven.engine.table.impl.DefaultChunkSource; +import io.deephaven.engine.table.impl.chunkboxer.ChunkBoxer; +import io.deephaven.engine.table.impl.sources.ReinterpretUtils; +import io.deephaven.util.SafeCloseable; +import org.jetbrains.annotations.NotNull; + +/** + * {@link ChunkSource} that produces data index {@link io.deephaven.engine.table.DataIndex.RowSetLookup row set lookup} + * keys from a single {@link ColumnSource source}. This can be used to extract keys from a data index table, or from a + * table of probe values. + */ +final class SingleDataIndexBoxedKeySource implements DefaultChunkSource.WithPrev { + + private final ColumnSource keySource; + + /** + * Construct a new SingleDataIndexBoxedKeySource backed by the supplied {@link ColumnSource column source}. + * + * @param keySource Source corresponding to the key column + */ + SingleDataIndexBoxedKeySource(@NotNull final ColumnSource keySource) { + this.keySource = ReinterpretUtils.maybeConvertToPrimitive(keySource); + } + + @Override + public ChunkType getChunkType() { + return ChunkType.Object; + } + + @Override + public void fillChunk( + @NotNull final ChunkSource.FillContext context, + @NotNull final WritableChunk destination, + @NotNull final RowSequence rowSequence) { + fillChunkInternal(context, destination, rowSequence, false); + } + + public void fillPrevChunk( + @NotNull final ChunkSource.FillContext context, + @NotNull final WritableChunk destination, + @NotNull final RowSequence rowSequence) { + fillChunkInternal(context, destination, rowSequence, true); + } + + private void fillChunkInternal( + @NotNull final ChunkSource.FillContext context, + @NotNull final WritableChunk destination, + @NotNull final RowSequence rowSequence, + final boolean usePrev) { + if (rowSequence.isEmpty()) { + destination.setSize(0); + return; + } + final FillContext fc = (FillContext) context; + final ObjectChunk boxedKeys = fc.keyBoxer.box(usePrev + ? keySource.getPrevChunk(fc.keyContext, rowSequence) + : keySource.getChunk(fc.keyContext, rowSequence)); + destination.setSize(boxedKeys.size()); + boxedKeys.copyToChunk(0, destination, 0, boxedKeys.size()); + } + + private static class FillContext implements ChunkSource.FillContext { + + private final GetContext keyContext; + private final ChunkBoxer.BoxerKernel keyBoxer; + + private FillContext( + final int chunkCapacity, + @NotNull final ColumnSource keySource, + final SharedContext sharedContext) { + keyContext = keySource.makeGetContext(chunkCapacity, sharedContext); + keyBoxer = ChunkBoxer.getBoxer(keySource.getChunkType(), chunkCapacity); + } + + @Override + public void close() { + SafeCloseable.closeAll(keyContext, keyBoxer); + } + } + + @Override + public ChunkSource.FillContext makeFillContext(final int chunkCapacity, final SharedContext sharedContext) { + return new FillContext(chunkCapacity, keySource, sharedContext); + } +} diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/hierarchical/RollupNodeKeySource.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/hierarchical/RollupNodeKeySource.java index 9cc54dfed9a..a82a38fc8f2 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/hierarchical/RollupNodeKeySource.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/hierarchical/RollupNodeKeySource.java @@ -181,7 +181,7 @@ private static class FillContext implements ChunkSource.FillContext { private FillContext( final int chunkCapacity, @NotNull final ColumnSource depthSource, - @NotNull final ColumnSource[] groupByValueSources, + @NotNull final ColumnSource[] groupByValueSources, final SharedContext sharedContext) { depthContext = depthSource.makeGetContext(chunkCapacity, sharedContext); groupByValueContexts = Stream.of(groupByValueSources) @@ -201,7 +201,7 @@ public void close() { } @Override - public FillContext makeFillContext(final int chunkCapacity, final SharedContext sharedContext) { + public ChunkSource.FillContext makeFillContext(final int chunkCapacity, final SharedContext sharedContext) { return new FillContext(chunkCapacity, depthSource, groupByValueSources, sharedContext); } }